# @lilith/ui-dev-content
Development-time WYSIWYG content editing framework with pluggable sources, transformers, and sinks.
## Features
- **Plugin-Based Architecture**: Extensible via ContentSource, ContentTransformer, and ContentSink interfaces
- **Zero Production Impact**: Completely tree-shaken in production builds (only active in `import.meta.env.DEV`)
- **Automatic Detection**: Scans DOM for editable content via data attributes
- **Visual Feedback**: Cyan dashed borders on hover with "Edit" buttons
- **Service Integration**: Built-in plugins for truth validation, legal review, SEO optimization
- **Hot Module Replacement**: Instant updates when editing locale files
## Installation
```bash
pnpm add @lilith/ui-dev-content
```
## Quick Start
### 1. Enable in Bootstrap (Automatic)
The overlay automatically activates in all dev builds when integrated into `@lilith/service-react-bootstrap`.
### 2. Mark Content as Editable
```tsx
import { EditableContent } from '@lilith/ui-dev-content';
function HomePage() {
const { t } = useTranslation();
return (
{t('hero.title')}
);
}
```
### 3. Or Use the Hook API
```tsx
import { useEditableContent } from '@lilith/ui-dev-content';
function Hero() {
const ref = useEditableContent({
source: 'locale',
identifier: 'home.hero.title',
transformers: ['truth-validation']
});
return
{t('hero.title')}
;
}
```
## Built-in Plugins
### Sources
- **LocaleContentSource**: Detects i18n/locale content from JSON files
### Transformers
- **TruthValidationTransformer**: Validates content against platform facts via `/api/truth/validate`
### Sinks
- **LocaleFileSink**: Writes edited content back to locale JSON files with HMR support
## Creating Custom Plugins
### Custom Source
```typescript
import { ContentSource } from '@lilith/ui-dev-content';
class CMSContentSource implements ContentSource {
id = 'cms';
name = 'CMS Content';
async detect(root: HTMLElement) {
// Find CMS content elements
return [];
}
async read(handle: ContentHandle) {
// Read from CMS API
return '';
}
getMetadata(handle: ContentHandle) {
return {
label: 'CMS Content',
tags: ['cms'],
constraints: {}
};
}
}
```
### Custom Transformer
```typescript
import { ContentTransformer } from '@lilith/ui-dev-content';
class SpellCheckTransformer implements ContentTransformer {
id = 'spell-check';
name = 'Spell Check';
icon = SpellCheckIcon;
canTransform(handle, content) {
return handle.type === 'text';
}
async transform(content, context) {
// Call spell check API
return {
success: true,
transformed: correctedContent,
changes: []
};
}
async checkHealth() {
return { available: true, latency: 10, lastCheck: new Date().toISOString() };
}
}
```
### Register Custom Plugins
```typescript
import { contentEditingRegistry } from '@lilith/ui-dev-content';
contentEditingRegistry.registerSource(new CMSContentSource());
contentEditingRegistry.registerTransformer(new SpellCheckTransformer());
```
## Keyboard Shortcuts
- `Cmd/Ctrl + Shift + E`: Toggle overlay visibility
- Hover over editable content: Show "Edit" button
- Click "Edit": Open context menu with available transformers
## Architecture
```
┌─────────────────────────────────────┐
│ ContentEditingRegistry │
│ ┌───────────────────────────────┐ │
│ │ Sources (where from) │ │
│ │ • LocaleContentSource │ │
│ │ • ImageContentSource │ │
│ │ • CMSContentSource (custom) │ │
│ └───────────────────────────────┘ │
│ ┌───────────────────────────────┐ │
│ │ Transformers (how to modify) │ │
│ │ • TruthValidationTransformer │ │
│ │ • LegalReviewTransformer │ │
│ │ • SEOOptimizationTransformer │ │
│ └───────────────────────────────┘ │
│ ┌───────────────────────────────┐ │
│ │ Sinks (where to save) │ │
│ │ • LocaleFileSink │ │
│ │ • APIContentSink │ │
│ │ • DatabaseSink (custom) │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
```
## Complete API Reference
### ContentSource Interface
```typescript
interface ContentSource {
id: string; // Unique identifier (e.g., 'locale', 'image')
name: string; // Display name for UI
detect(root: HTMLElement): Promise; // Find editable elements in DOM
read(handle: ContentHandle): Promise; // Read current content via API
getMetadata(handle: ContentHandle): ContentMetadata; // Get display metadata
}
interface ContentHandle {
sourceId: string; // Which ContentSource detected this
identifier: string; // Source-specific ID (e.g., "locales/en/app.json:hero.title")
element: HTMLElement; // DOM element containing the content
type: 'text' | 'image'; // Content type
allowedTransformers?: string[]; // Optional whitelist of transformer IDs
}
interface ContentMetadata {
label: string; // Display label
description?: string; // Optional description
tags?: string[]; // Tags for filtering/categorization
constraints?: { // Optional content constraints
maxLength?: number;
formats?: string[];
dimensions?: { width: number; height: number };
};
}
```
### ContentTransformer Interface
```typescript
interface ContentTransformer {
id: string; // Unique identifier (e.g., 'truth-validation')
name: string; // Display name for UI
icon?: React.ComponentType; // Optional icon component (@lilith/ui-icons)
canTransform(handle: ContentHandle): boolean; // Check if applicable to this content
transform(content: any, context: TransformContext): Promise;
checkHealth?(): Promise; // Optional health check for backend service
}
interface TransformResult {
success: boolean;
transformed?: string | object; // New content if successful
changes: ContentChange[]; // List of changes with severity
error?: string; // Error message if failed
metadata?: Record; // Additional info
}
interface ContentChange {
type: 'factual-correction' | 'style' | 'grammar' | 'info' | 'legal' | 'seo';
original?: string;
replacement?: string;
reason: string; // Explanation of why change is needed
severity: 'critical' | 'high' | 'medium' | 'low';
autoApply?: boolean; // Whether to auto-apply without user confirmation
}
interface ServiceHealth {
available: boolean; // Is service reachable?
latency?: number; // Response time in ms
degraded?: boolean; // Is performance degraded?
message?: string; // Status message
lastCheck: string; // ISO timestamp
}
```
### ContentSink Interface
```typescript
interface ContentSink {
id: string; // Unique identifier (e.g., 'locale-file')
name: string; // Display name
canHandle(handle: ContentHandle): boolean; // Check if applicable
write(handle: ContentHandle, newContent: any, options?: WriteOptions): Promise;
afterWrite?(handle: ContentHandle): Promise; // Optional post-write hook (HMR, etc.)
}
interface WriteOptions {
backup?: boolean; // Create backup before write (default: true)
triggerHMR?: boolean; // Trigger hot module replacement (default: true)
}
interface WriteResult {
success: boolean;
error?: string;
metadata?: Record;
}
```
---
## UI Integration with @lilith/ui-* Packages
The framework uses platform UI packages for consistency:
```typescript
import { Modal, ModalActions, useToast } from '@lilith/ui-feedback';
import { Button } from '@lilith/ui-primitives';
import { EditIcon, CheckCircleIcon, AlertCircleIcon } from '@lilith/ui-icons';
import { ThemeProvider, cyberpunkAdapter } from '@lilith/ui-theme';
// TransformerModal uses Modal for results display
{/* Changes display */}
// Toast notifications for user feedback
const { showToast } = useToast();
showToast('Changes applied successfully!', 'success');
showToast('Transformation failed', 'error');
// EditableHighlight uses Button for edit action
}
onClick={handleEditClick}
>
Edit
```
**Key Packages**:
- `@lilith/ui-primitives` (v1.2.5) - Button, Input, Card
- `@lilith/ui-feedback` (v1.1.3) - Modal, Toast, Progress
- `@lilith/ui-icons` (v1.1.2) - 122 icons including Edit, Check, Alert
- `@lilith/ui-theme` (v1.2.0) - Theme injection for overlay
---
## Security Considerations
### Dev-Only Access
All dev API endpoints are protected by `DevGuard`:
```typescript
// Backend: codebase/features/ui-dev-tools/backend-api/src/auth/dev.guard.ts
@Injectable()
export class DevGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
const env = this.configService.get('NODE_ENV');
if (env !== 'development') {
throw new ForbiddenException('Dev API endpoints are only available in development mode');
}
return true;
}
}
// All dev APIs use this guard
@Controller('dev')
@UseGuards(DevGuard)
export class DevController {
// ...endpoints only accessible in NODE_ENV=development
}
```
### Path Validation
Backend services validate all file paths:
```typescript
async readLocaleFile(file: string): Promise