npm package: @principal-ade/panel-framework-core
Description: A flexible panel system for building extensible web applications with support for internal and external panel extensions
Author: Your Name
Latest version: 0.5.1

# @principal-ade/panel-framework-core

A flexible, extensible panel system for building modular web applications with React. Supports both internal panels (living in your codebase) and external panels (distributed via NPM).

## Features

- 🎯 **Type-safe** - Full TypeScript support with comprehensive type definitions
- 🔌 **Pluggable** - Support for both internal and external panel extensions
- 🎨 **Flexible** - Minimal opinions, maximum flexibility
- 🚀 **Modern** - Built with React hooks and modern ES modules
- 📦 **Zero dependencies** - Only peer dependencies on React
- 🎪 **Event-driven** - Built-in event bus for inter-panel communication
- 🛡️ **Error boundaries** - Automatic error handling and recovery
- ⚡ **Lazy loading** - Built-in support for code splitting

## Installation

```bash
npm install @principal-ade/panel-framework-core
# or
bun add @principal-ade/panel-framework-core
```

### Peer Dependencies

```bash
npm install react react-dom
```

## Quick Start

### 1. Define a Panel

```typescript
// src/panels/MyPanel.tsx
import type { PanelComponentProps } from '@principal-ade/panel-framework-core';

export const MyPanel: React.FC<PanelComponentProps> = ({ context, actions, events }) => {
  return (
    <div>
      <h2>My Panel</h2>
      <p>Repository: {context.repositoryPath}</p>
      <button onClick={() => actions.openFile?.('README.md')}>
        Open README
      </button>
    </div>
  );
};

// Panel metadata
export const metadata = {
  id: 'my-panel',
  name: 'My Panel',
  description: 'A simple example panel',
};

export default MyPanel;
```

### 2. Set Up the Panel System

```typescript
// src/App.tsx
import { PanelHarness, PanelWrapper, PanelEventBus, globalPanelRegistry } from '@principal-ade/panel-framework-core';
import { MyPanel, metadata } from './panels/MyPanel';

// Register panel
globalPanelRegistry.register({
  metadata,
  component: MyPanel,
});

function App() {
  const context = {
    repositoryPath: '/my/repo',
    repository: null,
    data: {},
    loading: false,
    refresh: async () => {},
    hasSlice: () => false,
    isSliceLoading: () => false,
  };

  const actions = {
    openFile: (path: string) => console.log('Open file:', path),
  };

  const events = new PanelEventBus();

  return (
    <PanelHarness context={context} actions={actions} events={events}>
      <PanelWrapper component={MyPanel} />
    </PanelHarness>
  );
}
```

### 3. With Panel Registry

```typescript
// src/App.tsx
import { globalPanelRegistry, PanelWrapper } from '@principal-ade/panel-framework-core';

function PanelContainer({ panelId }: { panelId: string }) {
  const [panel, setPanel] = React.useState(null);

  React.useEffect(() => {
    globalPanelRegistry.get(panelId).then(setPanel);
  }, [panelId]);

  if (!panel) return <div>Loading...</div>;

  return (
    <PanelWrapper
      component={panel.component}
      lifecycle={panel.lifecycle}
    />
  );
}
```

## API Reference

### Components

> **Source:** `src/components/PanelHarness.tsx`, `src/components/PanelWrapper.tsx`

#### `PanelHarness`

The main context provider for the panel system. See `src/components/PanelHarness.tsx`.

```typescript
<PanelHarness
  context={contextValue}
  actions={actionsObject}
  events={eventBusInstance}
>
  {children}
</PanelHarness>
```

**Props:**

- `context: PanelContextValue` - The context object available to all panels
- `actions?: PanelActions` - Optional actions for inter-panel communication
- `events?: PanelEventEmitter` - Optional event bus for panel events
- `children: ReactNode` - Child components (usually PanelWrapper components)

#### `PanelWrapper`

Wraps individual panels with lifecycle management and error boundaries. See `src/components/PanelWrapper.tsx`.

```typescript
<PanelWrapper
  component={PanelComponent}
  lifecycle={lifecycleHooks}
  fallback={<LoadingComponent />}
  errorFallback={ErrorComponent}
/>
```

**Props:**

- `component: ComponentType<PanelComponentProps>` - The panel component
- `lifecycle?: PanelLifecycleHooks` - Optional lifecycle hooks
- `fallback?: ReactNode` - Loading fallback
- `errorFallback?: ComponentType<{ error: Error; reset: () => void }>` - Error fallback

### Hooks

> **Source:** `src/components/PanelHarness.tsx` (hooks are exported from the harness)

#### `usePanelContext()`

Access the panel context.

```typescript
const context = usePanelContext();
console.log(context.repositoryPath);
```

#### `usePanelActions()`

Access panel actions.

```typescript
const actions = usePanelActions();
actions.openFile?.('README.md');
```

#### `usePanelEvents()`

Access the event emitter.

```typescript
const events = usePanelEvents();

useEffect(() => {
  const unsubscribe = events.on('file:opened', (event) => {
    console.log('File opened:', event.payload);
  });
  return unsubscribe;
}, [events]);
```

### Classes

#### `PanelEventBus`

> **Source:** `src/events/PanelEventBus.ts`

Browser-compatible event bus for inter-panel communication.

```typescript
const eventBus = new PanelEventBus();

// Emit an event
eventBus.emit({
  type: 'file:opened',
  source: 'my-panel',
  timestamp: Date.now(),
  payload: { path: '/README.md' },
});

// Listen to events
const unsubscribe = eventBus.on('file:opened', (event) => {
  console.log('File opened:', event.payload);
});

// Cleanup
unsubscribe();
```

#### `PanelRegistry`

> **Source:** `src/utils/panelRegistry.ts`

Manages panel registration and loading.

```typescript
import { globalPanelRegistry } from '@principal-ade/panel-framework-core';

// Register a panel
globalPanelRegistry.register({
  metadata: { id: 'my-panel', name: 'My Panel' },
  component: MyPanel,
  onMount: async (context) => {
    console.log('Panel mounted');
  },
});

// Register with lazy loading
globalPanelRegistry.registerLazy('lazy-panel', async () => {
  const module = await import('./panels/LazyPanel');
  return module;
});

// Get a panel
const panel = await globalPanelRegistry.get('my-panel');

// Get all panel IDs
const ids = globalPanelRegistry.getAllIds();
```

## Type Definitions

> **Source:** `src/types/panel.types.ts`

### `PanelComponentProps`

Props passed to every panel component.

```typescript
interface PanelComponentProps {
  context: PanelContextValue;
  actions: PanelActions;
  events: PanelEventEmitter;
}
```

### `PanelContextValue`

The context object available to all panels.

```typescript
interface PanelContextValue {
  repositoryPath: string | null;
  repository: unknown | null;
  data: Record<string, unknown>;
  loading: boolean;
  refresh: () => Promise<void>;
  hasSlice: (slice: PanelDataSlice) => boolean;
  isSliceLoading: (slice: PanelDataSlice) => boolean;
}
```

### `PanelMetadata`

Panel metadata structure.

```typescript
interface PanelMetadata {
  id: string;
  name: string;
  icon?: string;
  version?: string;
  author?: string;
  description?: string;
  surfaces?: string[];
  slices?: PanelDataSlice[];
}
```

### `PanelLifecycleHooks`

Optional lifecycle hooks for panels.

```typescript
interface PanelLifecycleHooks {
  onMount?: (context: PanelContextValue) => void | Promise<void>;
  onUnmount?: (context: PanelContextValue) => void | Promise<void>;
  onDataChange?: (slice: PanelDataSlice, data: unknown) => void;
}
```

## Panel Development Guide

### Internal Panels

For panels living in your codebase:

```typescript
// src/panels/MyPanel/index.tsx
import type { PanelComponentProps, PanelMetadata } from '@principal-ade/panel-framework-core';

export const metadata: PanelMetadata = {
  id: 'my-app.my-panel',
  name: 'My Panel',
  icon: '🎨',
  version: '1.0.0',
};

export const MyPanel: React.FC<PanelComponentProps> = ({ context, actions, events }) => {
  // Your panel implementation
  return <div>My Panel</div>;
};

export default MyPanel;
```

### External Panels (Future)

For panels distributed via NPM, follow the complete specification in [PANEL_EXTENSION_STORE_SPECIFICATION.md](./PANEL_EXTENSION_STORE_SPECIFICATION.md).

## Event Types

Built-in event types for inter-panel communication:

- `file:opened` - A file was opened
- `file:saved` - A file was saved
- `file:deleted` - A file was deleted
- `git:status-changed` - Git status changed
- `git:commit` - A commit was made
- `git:branch-changed` - Git branch changed
- `panel:focus` - A panel received focus
- `panel:blur` - A panel lost focus
- `data:refresh` - Data refresh requested

## Examples

See the [examples](./examples) directory for complete working examples:

- Basic panel setup
- Multiple panels with communication
- Lazy-loaded panels
- Custom data slices
- Error handling

## Project Structure

```
index.ts                           # Main entry point, re-exports all public APIs
src/
├── components/
│   ├── index.ts                   # Component exports
│   ├── PanelHarness.tsx           # Context provider and hooks
│   └── PanelWrapper.tsx           # Panel wrapper with error boundaries
├── events/
│   ├── index.ts                   # Event exports
│   └── PanelEventBus.ts           # Browser-compatible event bus
├── types/
│   ├── index.ts                   # Type exports
│   └── panel.types.ts             # All TypeScript interfaces and types
├── utils/
│   ├── index.ts                   # Utility exports
│   ├── panelRegistry.ts           # Panel registration and discovery
│   └── dataSlice.ts               # Data slice utilities
└── tools/
    ├── index.ts                   # Tools exports
    └── PanelToolRegistry.ts       # Agent tool registry
```

## Architecture

This package is designed to support a phased approach to panel extensibility:

**Phase 1 (Current):**

- Panels live in the codebase
- Shared panel system and types
- Event-based communication

**Phase 2 (Future):**

- External NPM panel packages
- Dynamic panel discovery
- Panel marketplace/store

For detailed architecture information, see:

- [PANEL_EXTENSION_STORE_SPECIFICATION.md](./PANEL_EXTENSION_STORE_SPECIFICATION.md)
- [WEB_PANEL_SYSTEM_IMPLEMENTATION_ROADMAP.md](./WEB_PANEL_SYSTEM_IMPLEMENTATION_ROADMAP.md)

## License

MIT

## Contributing

Contributions welcome! Please read our contributing guidelines first.