@industry-theme/markdown-panels
v0.2.20
Published
Markdown rendering panels with industry theming support
Readme
Industry-Themed Markdown Panels
A panel extension for rendering markdown documents with industry theming support, built using @principal-ade/panel-framework-core.
This panel demonstrates how to integrate themed-markdown with the panel framework, following patterns from the desktop-app's MarkdownRenderingPanel and GitHubReadmePanel.
Features
- Themed Markdown Rendering: Uses
themed-markdownwith@principal-ade/industry-themefor consistent, professional styling - Multiple View Modes: Switch between document view (all content) and slide view (paginated)
- Font Size Controls: Adjustable font scaling from 50% to 300%
- Slide Navigation: Navigate through markdown slides with previous/next controls
- Panel Framework Integration: Full integration with context, actions, and events
- Responsive Design: Adapts to different panel sizes and layouts
Installation
npm installDevelopment
# Development mode (watch for changes)
npm run dev
# Build for production
npm run build
# Type checking
npm run typecheck
# Linting
npm run lint
# Storybook
npm run storybookProject Structure
industry-themed-markdown-panels/
├── src/
│ ├── panels/
│ │ └── MarkdownPanel.tsx # Main markdown panel component
│ ├── types/
│ │ └── index.ts # TypeScript type definitions
│ ├── mocks/
│ │ └── panelContext.tsx # Mock providers for Storybook
│ └── index.tsx # Panel registration
├── dist/
│ ├── panels.bundle.js # Built panel bundle
│ └── index.d.ts # TypeScript declarations
├── package.json
├── vite.config.ts
└── README.mdUsage
The panel exports a single MarkdownPanel that demonstrates:
- How to use
DocumentViewfromthemed-markdown - How to integrate with
@principal-ade/industry-theme - How to parse markdown into slides using
parseMarkdownIntoPresentation - How to build interactive panel controls
- How to access panel context for repository information
Panel Definition
{
id: 'principal-ade.markdown-viewer',
name: 'Markdown Viewer',
icon: '📄',
description: 'Themed markdown rendering panel with document and slide views',
component: MarkdownPanel
}Panel Component API
PanelComponentProps
Every panel component receives these props:
interface PanelComponentProps {
// Access to shared data and state
context: PanelContextValue;
// Actions for host interaction
actions: PanelActions;
// Event system for inter-panel communication
events: PanelEventEmitter;
}Context
Access repository data and state:
const { context } = props;
// Repository information
context.repositoryPath // Current repository path
context.repository // Repository metadata
// Data slices
context.gitStatus // Git status information
context.fileTree // File tree structure
context.markdownFiles // Markdown files list
// State management
context.loading // Loading state
context.refresh() // Refresh data
context.hasSlice('git') // Check slice availabilityActions
Interact with the host application:
const { actions } = props;
// File operations
actions.openFile?.('path/to/file.ts');
actions.openGitDiff?.('path/to/file.ts', 'unstaged');
// Navigation
actions.navigateToPanel?.('panel-id');
// Notifications
actions.notifyPanels?.(event);Events
Subscribe to and emit panel events:
const { events } = props;
// Subscribe to events
useEffect(() => {
const unsubscribe = events.on('file:opened', (event) => {
console.log('File opened:', event.payload);
});
return unsubscribe; // Cleanup
}, [events]);
// Emit events
events.emit({
type: 'custom:event',
source: 'my-panel',
timestamp: Date.now(),
payload: { data: 'value' },
});Panel Definition
Each panel must be defined with metadata:
interface PanelDefinition {
id: string; // Unique ID (e.g., 'org.panel-name')
name: string; // Display name
icon?: string; // Icon (emoji or URL)
version?: string; // Version (defaults to package.json)
author?: string; // Author (defaults to package.json)
description?: string; // Short description
component: React.FC; // The panel component
// Optional lifecycle hooks
onMount?: (context) => void | Promise<void>;
onUnmount?: (context) => void | Promise<void>;
onDataChange?: (slice, data) => void;
}Lifecycle Hooks
Per-Panel Hooks
Called for individual panels:
{
id: 'my-panel',
component: MyPanel,
onMount: async (context) => {
console.log('Panel mounted');
if (context.hasSlice('git')) {
await context.refresh();
}
},
onUnmount: async (context) => {
console.log('Panel unmounting');
// Cleanup logic
},
onDataChange: (slice, data) => {
console.log(`Data changed: ${slice}`, data);
},
}Package-Level Hooks
Called once for the entire package:
export const onPackageLoad = async () => {
console.log('Package loaded');
// Initialize shared resources
};
export const onPackageUnload = async () => {
console.log('Package unloading');
// Cleanup shared resources
};Building and Publishing
Build Configuration
The build process (via Vite) automatically:
- Externalizes React and ReactDOM (provided by host)
- Bundles all other dependencies
- Generates TypeScript declarations
- Creates source maps
- Outputs to
dist/panels.bundle.js
Local Testing
Link your panel locally for testing:
# In your panel directory
bun run build
bun link
# In your host application
bun link @your-org/your-panel-namePublishing to NPM
# Build the package
bun run build
# Verify the output
ls -la dist/
# Publish to NPM
npm publish --access publicInstalling in Host Application
# In the host application
npm install @your-org/your-panel-nameThe host application will automatically discover your panel by the panel-extension keyword in package.json.
Best Practices
1. Namespaced Panel IDs
Use reverse domain notation for panel IDs:
id: 'com.company.feature-panel' // ✅ Good
id: 'my-panel' // ❌ Bad (collision risk)2. Error Handling
Always handle errors gracefully:
const [error, setError] = useState(null);
useEffect(() => {
const loadData = async () => {
try {
if (!context.hasSlice('git')) {
throw new Error('Git data not available');
}
// Use data...
} catch (err) {
setError(err);
}
};
loadData();
}, [context]);
if (error) {
return <div>Error: {error.message}</div>;
}3. Loading States
Show loading indicators:
if (context.loading || context.isSliceLoading('git')) {
return <div>Loading...</div>;
}4. Cleanup Subscriptions
Always unsubscribe from events:
useEffect(() => {
const unsubscribe = events.on('event:type', handler);
return unsubscribe; // Cleanup on unmount
}, [events]);5. Type Safety
Use provided types for type safety:
import type { PanelComponentProps, GitStatus } from './types';
const MyPanel: React.FC<PanelComponentProps> = ({ context }) => {
const gitStatus: GitStatus = context.gitStatus;
// ...
};Available Data Slices
Panels can access these data slices from the host:
| Slice | Type | Description |
|-------|------|-------------|
| git | GitStatus | Git repository status |
| markdown | MarkdownFile[] | Markdown files in repository |
| fileTree | FileTree | File system tree structure |
| packages | PackageLayer[] | Package dependencies |
| quality | QualityMetrics | Code quality metrics |
Check availability before use:
if (context.hasSlice('git') && !context.isSliceLoading('git')) {
// Use git data
}Event Types
Standard panel events:
| Event | Description | Payload |
|-------|-------------|---------|
| file:opened | File was opened | { filePath: string } |
| file:saved | File was saved | { filePath: string } |
| file:deleted | File was deleted | { filePath: string } |
| git:status-changed | Git status changed | GitStatus |
| git:commit | Git commit made | { hash: string } |
| git:branch-changed | Branch changed | { branch: string } |
| panel:focus | Panel gained focus | { panelId: string } |
| panel:blur | Panel lost focus | { panelId: string } |
| data:refresh | Data was refreshed | { slices: string[] } |
Dependencies
Peer Dependencies (Required)
These are provided by the host application:
react>= 18.0.0react-dom>= 18.0.0
Optional Peer Dependencies
@principal-ade/panel-framework-core- For advanced panel features
Bundled Dependencies
Include any libraries unique to your panel:
{
"dependencies": {
"lodash": "^4.17.21",
"date-fns": "^2.29.0",
"your-custom-lib": "^1.0.0"
}
}These will be bundled into your panel output.
Troubleshooting
Panel Not Discovered
Ensure package.json has:
{
"keywords": ["panel-extension"],
"main": "dist/panels.bundle.js"
}Build Errors
Check that peer dependencies are externalized in vite.config.ts:
external: ['react', 'react-dom']Type Errors
Ensure TypeScript can find types:
bun run typecheckRuntime Errors
Check browser console and ensure:
- Panel ID is unique
- Required exports are present (
panelsarray) - Component is a valid React component
Resources
License
MIT © Your Name
Contributing
Contributions welcome! Please read the contributing guidelines first.
Support
For issues and questions:
