Markdown rendering panels with industry theming support
npm install @industry-theme/markdown-panelsA 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.
- Themed Markdown Rendering: Uses themed-markdown with @principal-ade/industry-theme for 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
``bash`
npm install
`bashDevelopment mode (watch for changes)
npm run dev
Project 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.md
`Usage
The panel exports a single
MarkdownPanel that demonstrates:1. How to use
DocumentView from themed-markdown
2. How to integrate with @principal-ade/industry-theme
3. How to parse markdown into slides using parseMarkdownIntoPresentation
4. How to build interactive panel controls
5. How to access panel context for repository informationPanel Definition
`typescript
{
id: 'principal-ade.markdown-viewer',
name: 'Markdown Viewer',
icon: '📄',
description: 'Themed markdown rendering panel with document and slide views',
component: MarkdownPanel
}
`Panel Component API
$3
Every panel component receives these props:
`typescript
interface PanelComponentProps {
// Access to shared data and state
context: PanelContextValue; // Actions for host interaction
actions: PanelActions;
// Event system for inter-panel communication
events: PanelEventEmitter;
}
`$3
Access repository data and state:
`tsx
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 availability
`$3
Interact with the host application:
`tsx
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);
`$3
Subscribe to and emit panel events:
`tsx
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:
`typescript
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;
onUnmount?: (context) => void | Promise;
onDataChange?: (slice, data) => void;
}
`Lifecycle Hooks
$3
Called for individual panels:
`typescript
{
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);
},
}
`$3
Called once for the entire package:
`typescript
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
$3
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$3
Link your panel locally for testing:
`bash
In your panel directory
bun run build
bun linkIn your host application
bun link @your-org/your-panel-name
`$3
`bash
Build the package
bun run buildVerify the output
ls -la dist/Publish to NPM
npm publish --access public
`$3
`bash
In the host application
npm install @your-org/your-panel-name
`The host application will automatically discover your panel by the
panel-extension keyword in package.json.Best Practices
$3
Use reverse domain notation for panel IDs:
`typescript
id: 'com.company.feature-panel' // ✅ Good
id: 'my-panel' // ❌ Bad (collision risk)
`$3
Always handle errors gracefully:
`tsx
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
Error: {error.message};
}
`$3
Show loading indicators:
`tsx
if (context.loading || context.isSliceLoading('git')) {
return Loading...;
}
`$3
Always unsubscribe from events:
`tsx
useEffect(() => {
const unsubscribe = events.on('event:type', handler);
return unsubscribe; // Cleanup on unmount
}, [events]);
`$3
Use provided types for type safety:
`tsx
import type { PanelComponentProps, GitStatus } from './types';const MyPanel: React.FC = ({ 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:
`tsx
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
$3
These are provided by the host application:
-
react >= 18.0.0
- react-dom >= 18.0.0$3
-
@principal-ade/panel-framework-core - For advanced panel features$3
Include any libraries unique to your panel:
`json
{
"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
$3
Ensure
package.json has:
`json
{
"keywords": ["panel-extension"],
"main": "dist/panels.bundle.js"
}
`$3
Check that peer dependencies are externalized in
vite.config.ts:
`typescript
external: ['react', 'react-dom']
`$3
Ensure TypeScript can find types:
`bash
bun run typecheck
`$3
Check browser console and ensure:
- Panel ID is unique
- Required exports are present (
panels` array)- Panel Extension Store Specification V2
- Panel Framework Core
- Example Implementations
MIT © Your Name
Contributions welcome! Please read the contributing guidelines first.
For issues and questions:
- GitHub Issues
- Discussions