Instruction file imported from sebi75/portal-bubble (
.cursor/rules/project-overview.mdc). Copyright stays with the author.
Portal Bubble - Project Overview
What is Portal Bubble?
A generic embeddable widget for displaying content in an expandable bubble. Built to be configurable and non-opinionated about data structures.
Quick Facts
- Language: TypeScript 5.1.6
- Runtime: Bun (preferred) or Node.js 16+
- Build Tool: Webpack 5
- Bundle Size: ~46KB minified
- License: MIT
- Dependencies: None (zero runtime dependencies)
Key Features
- Generic message protocol (kebab-case events)
- Optional caching (disabled by default)
- No enforced data structures
- Configurable endpoints
- Custom event handlers
- Data transformers
- Server as source of truth
Project Structure
portal-bubble/
├── src/
│ ├── components/ # UI components
│ │ ├── widget-bubble.ts
│ │ ├── widget-iframe.ts
│ │ └── widget-prompters.ts
│ ├── lib/ # Business logic
│ │ ├── widget-data.manager.ts
│ │ ├── widget-state.manager.ts
│ │ ├── message-handler.ts
│ │ ├── storage.service.ts
│ │ └── styles-manager.ts
│ ├── interfaces/ # TypeScript interfaces
│ │ └── storage.interface.ts
│ ├── types/ # Type definitions
│ │ ├── action-types.ts
│ │ ├── embed-init.ts
│ │ └── local-storage.ts
│ ├── config/ # Constants
│ │ └── constants.ts
│ ├── utils/ # Pure functions
│ │ └── index.ts
│ └── index.ts # Entry point
├── dist/ # Build output
├── .cursor/rules/ # Project documentation
├── README.md # User documentation
├── CONTRIBUTING.md # Contributor guide
├── LICENSE # MIT License
└── example.html # Usage example
Core Components
| Component | Purpose | File |
|---|---|---|
| WidgetBubble | Expandable bubble button | components/widget-bubble.ts |
| WidgetIframe | Embedded content container | components/widget-iframe.ts |
| WidgetPrompters | Optional prompt suggestions | components/widget-prompters.ts |
| WidgetDataManager | Config fetch & caching | lib/widget-data.manager.ts |
| WidgetStateManager | UI state coordination | lib/widget-state.manager.ts |
| MessageHandler | Event routing | lib/message-handler.ts |
| StorageService | Optional caching | lib/storage.service.ts |
| StylesManager | CSS injection | lib/styles-manager.ts |
Architecture Layers
┌─────────────────────────────────────┐
│ Presentation Layer │
│ (Components render UI) │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ Business Logic Layer │
│ (Managers, MessageHandler) │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ Data Layer │
│ (Optional StorageService) │
└───────────────┬─────────────────────┘
↓
┌─────────────────────────────────────┐
│ Configuration Layer │
│ (Constants, Types) │
└─────────────────────────────────────┘
Key Principles
- SOLID - Each class has one responsibility
- DRY - Constants centralized, code reused
- Generic - No enforced schemas
- Configurable - User controls behavior
- Server-first - Optional client caching
Common Commands
bun install # Install dependencies
bun run dev # Watch mode
bun run build # Production build
bun run build:dev # Development build
bun test # Run tests
Basic Integration
<script>
window.portalBubbleConfig = {
id: "your-widget-id",
baseUrl: "https://api.example.com",
};
</script>
<script src="./dist/embed.js"></script>
Configuration Structure
window.portalBubbleConfig = {
// Required
id: string;
baseUrl: string;
// Optional
configEndpoint?: string;
chatEndpoint?: string;
enableCache?: boolean;
cacheExpiration?: { config: number; data: number };
iframeParams?: Record<string, string>;
onMessage?: Record<string, (data: any) => void>;
transformers?: {
outgoing?: (type: string, data: any) => any;
incoming?: (type: string, data: any) => any;
};
styles?: object;
debug?: boolean;
};
Server Response
Minimal config (all fields optional):
{
bubbleColor?: string;
isOpenByDefault?: boolean;
bubbleIcon?: string;
prompters?: string[];
// Add any custom fields
}
Message Protocol
Events use kebab-case:
| Event | Direction | Purpose |
|---|---|---|
open-chat |
Iframe → Widget | Open widget |
close-chat |
Iframe → Widget | Close widget |
widget-ready |
Iframe → Widget | Ready for data |
init-data |
Widget → Iframe | Send config/cache |
data-update |
Both | Generic data update |
| Custom events | Both | User-defined |
Caching
Default: Disabled
Enable if needed:
enableCache: true,
cacheExpiration: {
config: 5 * 60 * 1000,
data: 7 * 24 * 60 * 60 * 1000
}
File Naming
- Components:
widget-*.ts - Managers:
*.manager.ts - Services:
*.service.ts - Interfaces:
*.interface.ts - Types: descriptive name in
types/
Code Naming
// Classes
class WidgetBubble {}
// Interfaces
interface IStorageService {}
// Types
type WidgetCache<T> = {};
// Constants
const CACHE_EXPIRATION = {};
// Events (kebab-case)
const StandardEvents = {
READY: "widget-ready",
};
// Functions
function getCachedData() {}
Important Files
For Users
README.md- Usage documentationexample.html- Working exampleLICENSE- MIT license
For Contributors
CONTRIBUTING.md- How to contribute.cursor/rules/architecture.mdc- Architecture details
For Development
src/index.ts- Entry pointsrc/config/constants.ts- All constantswebpack.config.ts- Build configtsconfig.json- TypeScript config
Common Tasks
Add a Constant
Edit src/config/constants.ts
Add an Event
Use any string in onMessage config - no enum needed
Add a Component
- Create in
src/components/ - Follow existing pattern
- Accept minimal config
- Integrate in
src/index.ts
Modify Styles
Update src/config/constants.ts or override via config
Testing Locally
bun run build- Open
example.html - Check console (debug mode enabled)
- Verify widget appears
Debugging
Enable debug mode:
window.portalBubbleConfig = {
debug: true,
};
Then check console for all events and state changes.
Security
- Origin validation on all messages
- XSS protection (textContent only)
- No eval or unsafe operations
- CSP compatible
Performance
- Lazy rendering (iframe on demand)
- Optional caching
- Minimal bundle (~46KB)
- No external dependencies
Browser Support
- Chrome/Edge (latest)
- Firefox (latest)
- Safari (latest)
- Mobile browsers
Version
Current: 2.0.0 Status: Production ready
Philosophy
Portal Bubble is a foundation, not a complete solution. It provides:
- UI (bubble, iframe, prompters)
- State management
- Message routing
- Optional caching
You provide:
- Data structures
- Event handling
- Backend API
- Business logic
The widget adapts to your needs, not the other way around.
Getting Help
- Read
README.md - Check
example.html - Review
.cursor/rules/architecture.mdc - Open GitHub issue
License
MIT License
Remember: This is a generic widget - define your own data structures, events, and behavior through configuration.