create-lego-one 2.0.12 → 2.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +150 -15
- package/dist/index.cjs.map +1 -1
- package/package.json +1 -1
- package/template/.cursor/rules/rules.mdc +639 -0
- package/template/.dockerignore +58 -0
- package/template/.env.example +18 -0
- package/template/.eslintignore +5 -0
- package/template/.eslintrc.js +28 -0
- package/template/.prettierignore +6 -0
- package/template/.prettierrc +11 -0
- package/template/CLAUDE.md +634 -0
- package/template/Dockerfile +67 -0
- package/template/PROMPT.md +457 -0
- package/template/README.md +325 -0
- package/template/docker-compose.yml +48 -0
- package/template/docker-entrypoint.sh +23 -0
- package/template/docs/checkpoints/.template.md +64 -0
- package/template/docs/checkpoints/framework/01-infrastructure-setup.md +132 -0
- package/template/docs/checkpoints/framework/02-pocketbase-setup.md +155 -0
- package/template/docs/checkpoints/framework/03-host-kernel.md +170 -0
- package/template/docs/checkpoints/framework/04-auth-system.md +163 -0
- package/template/docs/checkpoints/framework/phase-05-multitenancy-rbac.md +223 -0
- package/template/docs/checkpoints/framework/phase-06-ui-components.md +260 -0
- package/template/docs/checkpoints/framework/phase-07-communication-system.md +276 -0
- package/template/docs/checkpoints/framework/phase-08-plugin-system.md +91 -0
- package/template/docs/checkpoints/framework/phase-09-dashboard-plugin.md +111 -0
- package/template/docs/checkpoints/framework/phase-10-todo-plugin.md +169 -0
- package/template/docs/checkpoints/framework/phase-11-testing.md +264 -0
- package/template/docs/checkpoints/framework/phase-12-deployment.md +294 -0
- package/template/docs/checkpoints/framework/phase-13-documentation.md +312 -0
- package/template/docs/framework/plans/00-index.md +164 -0
- package/template/docs/framework/plans/01-infrastructure-setup.md +855 -0
- package/template/docs/framework/plans/02-pocketbase-setup.md +1374 -0
- package/template/docs/framework/plans/03-host-kernel.md +1518 -0
- package/template/docs/framework/plans/04-auth-system.md +1466 -0
- package/template/docs/framework/plans/05-multitenancy-rbac.md +1527 -0
- package/template/docs/framework/plans/06-ui-components.md +1478 -0
- package/template/docs/framework/plans/07-communication-system.md +1106 -0
- package/template/docs/framework/plans/08-plugin-system.md +1179 -0
- package/template/docs/framework/plans/09-dashboard-plugin.md +1137 -0
- package/template/docs/framework/plans/10-todo-plugin.md +1343 -0
- package/template/docs/framework/plans/11-testing.md +935 -0
- package/template/docs/framework/plans/12-deployment.md +896 -0
- package/template/docs/framework/prompts/0-boilerplate-modernjs.md +151 -0
- package/template/docs/framework/research/00-modernjs-audit.md +488 -0
- package/template/docs/framework/research/01-system-blueprint.md +721 -0
- package/template/docs/framework/research/02-data-migration-protocol.md +699 -0
- package/template/docs/framework/research/03-host-setup.md +714 -0
- package/template/docs/framework/research/04-plugin-architecture.md +645 -0
- package/template/docs/framework/research/05-slot-injection-pattern.md +671 -0
- package/template/docs/framework/research/06-cli-strategy.md +615 -0
- package/template/docs/framework/research/07-deployment.md +629 -0
- package/template/docs/framework/research/README.md +282 -0
- package/template/docs/framework/setup/00-index.md +210 -0
- package/template/docs/framework/setup/01-framework-structure.md +308 -0
- package/template/docs/framework/setup/02-development-workflow.md +405 -0
- package/template/docs/framework/setup/03-environment-setup.md +215 -0
- package/template/docs/framework/setup/04-kernel-architecture.md +499 -0
- package/template/docs/framework/setup/05-plugin-system.md +620 -0
- package/template/docs/framework/setup/06-communication-patterns.md +451 -0
- package/template/docs/framework/setup/07-plugin-development.md +582 -0
- package/template/docs/framework/setup/08-component-library.md +658 -0
- package/template/docs/framework/setup/09-data-integration.md +609 -0
- package/template/docs/framework/setup/10-auth-rbac.md +497 -0
- package/template/docs/framework/setup/11-hooks-api.md +393 -0
- package/template/docs/framework/setup/12-components-api.md +665 -0
- package/template/docs/framework/setup/13-deployment-guide.md +566 -0
- package/template/docs/framework/setup/README.md +548 -0
- package/template/host/package.json +1 -1
- package/template/nginx.conf +72 -0
- package/template/package.json +1 -1
- package/template/packages/plugins/@lego/plugin-dashboard/package.json +1 -1
- package/template/packages/plugins/@lego/plugin-todo/package.json +1 -1
- package/template/pocketbase/CHANGELOG.md +911 -0
- package/template/pocketbase/LICENSE.md +17 -0
- package/template/scripts/create-plugin.js +221 -0
- package/template/scripts/deploy.sh +56 -0
- package/template/tsconfig.base.json +26 -0
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
# Communication Patterns
|
|
2
|
+
|
|
3
|
+
**Window Bridge and Channel Bus for Plugin-Host Communication**
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
The Lego-One framework uses a **Window Bridge pattern** for plugin-host communication. Plugins access host services and state via global window objects, and communicate through a **Garfish Channel Bus** for pub/sub messaging.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Architecture Diagram
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
17
|
+
│ HOST KERNEL │
|
|
18
|
+
│ ┌────────────────────────────────────────────────────────────────────┐ │
|
|
19
|
+
│ │ CHANNEL BUS (Garfish) │ │
|
|
20
|
+
│ │ • publish(channel, message) • subscribe(channel, callback) │ │
|
|
21
|
+
│ └────────────────────────────────────────────────────────────────────┘ │
|
|
22
|
+
│ │ │
|
|
23
|
+
│ ┌────────────────────────────────────────────────────────────────────┐ │
|
|
24
|
+
│ │ WINDOW BRIDGE (global) │ │
|
|
25
|
+
│ │ • __LEGO_KERNEL_STATE__ (read-only state access) │ │
|
|
26
|
+
│ │ • __LEGO_CHANNEL_BUS__ (pub/sub messaging) │ │
|
|
27
|
+
│ └────────────────────────────────────────────────────────────────────┘ │
|
|
28
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
29
|
+
│
|
|
30
|
+
═══════════════════════════════════════
|
|
31
|
+
PLUGIN BOUNDARY (Window Object)
|
|
32
|
+
═══════════════════════════════════════
|
|
33
|
+
│
|
|
34
|
+
┌─────────────────────────────────────────────────────────────────────────┐
|
|
35
|
+
│ PLUGIN │
|
|
36
|
+
│ ┌────────────────────────────────────────────────────────────────────┐ │
|
|
37
|
+
│ │ PLUGIN CODE │ │
|
|
38
|
+
│ │ const state = window.__LEGO_KERNEL_STATE__ │ │
|
|
39
|
+
│ │ const bus = window.__LEGO_CHANNEL_BUS__ │ │
|
|
40
|
+
│ │ bus.publish('lego:toast', {...}) │ │
|
|
41
|
+
│ │ bus.subscribe('lego:auth', (data) => ...) │ │
|
|
42
|
+
│ └────────────────────────────────────────────────────────────────────┘ │
|
|
43
|
+
└─────────────────────────────────────────────────────────────────────────┘
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Window Bridge
|
|
49
|
+
|
|
50
|
+
### __LEGO_KERNEL_STATE__
|
|
51
|
+
|
|
52
|
+
**Purpose:** Read-only access to kernel global state
|
|
53
|
+
|
|
54
|
+
**Available Properties:**
|
|
55
|
+
```typescript
|
|
56
|
+
interface KernelState {
|
|
57
|
+
user: User | null; // Current authenticated user
|
|
58
|
+
organization: Organization | null; // Current selected organization
|
|
59
|
+
token: string | null; // Authentication token
|
|
60
|
+
isAuthenticated: boolean; // Auth status flag
|
|
61
|
+
sidebarOpen: boolean; // Sidebar toggle state
|
|
62
|
+
mobileMenuOpen: boolean; // Mobile menu state
|
|
63
|
+
|
|
64
|
+
// Access method to Zustand store
|
|
65
|
+
useGlobalKernelState: typeof import('@lego/kernel/shared-state').useGlobalKernelState;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Accessing State:**
|
|
70
|
+
```typescript
|
|
71
|
+
// In plugin component
|
|
72
|
+
function MyPluginComponent() {
|
|
73
|
+
// Method 1: Direct property access
|
|
74
|
+
const kernelState = window.__LEGO_KERNEL_STATE__;
|
|
75
|
+
const user = kernelState?.user;
|
|
76
|
+
const organization = kernelState?.organization;
|
|
77
|
+
|
|
78
|
+
// Method 2: Get latest state from store
|
|
79
|
+
const latestState = kernelState?.useGlobalKernelState?.getState();
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div>
|
|
83
|
+
<p>User: {user?.email}</p>
|
|
84
|
+
<p>Organization: {organization?.name}</p>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**What You CAN Do:**
|
|
91
|
+
- ✅ Read user info
|
|
92
|
+
- ✅ Read organization info
|
|
93
|
+
- ✅ Check authentication status
|
|
94
|
+
- ✅ Get latest state from store
|
|
95
|
+
|
|
96
|
+
**What You CANNOT Do:**
|
|
97
|
+
- ❌ Modify kernel state directly
|
|
98
|
+
- ❌ Call kernel actions (use channels instead)
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
### __LEGO_CHANNEL_BUS__
|
|
103
|
+
|
|
104
|
+
**Purpose:** Pub/sub messaging between plugins and host
|
|
105
|
+
|
|
106
|
+
**Available Methods:**
|
|
107
|
+
```typescript
|
|
108
|
+
interface ChannelBus {
|
|
109
|
+
publish: (channel: string, message: any) => void;
|
|
110
|
+
subscribe: (channel: string, callback: (message: any) => void) => () => void;
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Publishing Messages:**
|
|
115
|
+
```typescript
|
|
116
|
+
// Publish toast notification
|
|
117
|
+
window.__LEGO_CHANNEL_BUS__?.publish('lego:toast', {
|
|
118
|
+
type: 'success',
|
|
119
|
+
title: 'Saved!',
|
|
120
|
+
description: 'Your changes were saved successfully',
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Publish navigation request
|
|
124
|
+
window.__LEGO_CHANNEL_BUS__?.publish('lego:navigation', {
|
|
125
|
+
path: '/todos/123',
|
|
126
|
+
replace: false,
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**Subscribing to Messages:**
|
|
131
|
+
```typescript
|
|
132
|
+
useEffect(() => {
|
|
133
|
+
const channelBus = window.__LEGO_CHANNEL_BUS__;
|
|
134
|
+
if (!channelBus) return;
|
|
135
|
+
|
|
136
|
+
// Subscribe to auth changes
|
|
137
|
+
const unsubscribe = channelBus.subscribe('lego:auth:change', (data) => {
|
|
138
|
+
console.log('Auth changed:', data);
|
|
139
|
+
|
|
140
|
+
if (data.isAuthenticated) {
|
|
141
|
+
console.log('User logged in:', data.userId);
|
|
142
|
+
// Refresh plugin data
|
|
143
|
+
} else {
|
|
144
|
+
console.log('User logged out');
|
|
145
|
+
// Clear plugin data
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Cleanup on unmount
|
|
150
|
+
return () => {
|
|
151
|
+
unsubscribe();
|
|
152
|
+
};
|
|
153
|
+
}, []);
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## Channel Names
|
|
159
|
+
|
|
160
|
+
### Standard Channels
|
|
161
|
+
|
|
162
|
+
| Channel | Purpose | Published By |
|
|
163
|
+
|---------|---------|-------------|
|
|
164
|
+
| `lego:toast` | Show notifications | Plugins → Host |
|
|
165
|
+
| `lego:navigation` | Navigate to route | Plugins → Host |
|
|
166
|
+
| `lego:state:update` | Update shared state | Plugins → Host |
|
|
167
|
+
| `lego:plugin:ready` | Plugin initialized | Plugins → Host |
|
|
168
|
+
| `lego:plugin:error` | Plugin error | Plugins → Host |
|
|
169
|
+
| `lego:auth:change` | Auth state changed | Host → All |
|
|
170
|
+
| `lego:organization:change` | Organization changed | Host → All |
|
|
171
|
+
|
|
172
|
+
### Channel Message Formats
|
|
173
|
+
|
|
174
|
+
**Toast Channel:**
|
|
175
|
+
```typescript
|
|
176
|
+
{
|
|
177
|
+
type: 'success' | 'error' | 'info' | 'warning',
|
|
178
|
+
title: string,
|
|
179
|
+
description?: string,
|
|
180
|
+
duration?: number, // milliseconds
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Navigation Channel:**
|
|
185
|
+
```typescript
|
|
186
|
+
{
|
|
187
|
+
path: string,
|
|
188
|
+
replace?: boolean,
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Auth Change Channel:**
|
|
193
|
+
```typescript
|
|
194
|
+
{
|
|
195
|
+
isAuthenticated: boolean,
|
|
196
|
+
userId?: string,
|
|
197
|
+
organizationId?: string,
|
|
198
|
+
}
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Organization Change Channel:**
|
|
202
|
+
```typescript
|
|
203
|
+
{
|
|
204
|
+
organizationId: string,
|
|
205
|
+
organizationName: string,
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## Communication Patterns
|
|
212
|
+
|
|
213
|
+
### Pattern 1: Plugin → Host (Request Action)
|
|
214
|
+
|
|
215
|
+
**Use Case:** Plugin wants host to display a toast notification
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
// In plugin
|
|
219
|
+
function saveTodo() {
|
|
220
|
+
// After successful save
|
|
221
|
+
window.__LEGO_CHANNEL_BUS__?.publish('lego:toast', {
|
|
222
|
+
type: 'success',
|
|
223
|
+
title: 'Todo Created',
|
|
224
|
+
description: 'Your todo was created successfully',
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
**Host Integration:**
|
|
230
|
+
- Host has `ToastIntegration` component listening to `lego:toast`
|
|
231
|
+
- Automatically displays Shadcn toast when message received
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
### Pattern 2: Host → Plugin (Broadcast State)
|
|
236
|
+
|
|
237
|
+
**Use Case:** User switches organizations, plugin needs to refresh data
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
// In plugin
|
|
241
|
+
useEffect(() => {
|
|
242
|
+
const channelBus = window.__LEGO_CHANNEL_BUS__;
|
|
243
|
+
if (!channelBus) return;
|
|
244
|
+
|
|
245
|
+
const unsubscribe = channelBus.subscribe('lego:organization:change', (data) => {
|
|
246
|
+
console.log('Organization changed to:', data.organizationName);
|
|
247
|
+
|
|
248
|
+
// Refresh plugin data for new organization
|
|
249
|
+
queryClient.invalidateQueries({ queryKey: ['todos'] });
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
return unsubscribe;
|
|
253
|
+
}, []);
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
**Host Behavior:**
|
|
257
|
+
- `ChannelProvider` automatically publishes org changes
|
|
258
|
+
- All subscribed plugins receive notification
|
|
259
|
+
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
### Pattern 3: Plugin → Plugin (Direct Communication)
|
|
263
|
+
|
|
264
|
+
**Use Case:** Plugin A needs to notify Plugin B
|
|
265
|
+
|
|
266
|
+
**Option 1: Via Host (Recommended)**
|
|
267
|
+
```typescript
|
|
268
|
+
// Plugin A publishes
|
|
269
|
+
window.__LEGO_CHANNEL_BUS__?.publish('custom:plugin-a-event', {
|
|
270
|
+
data: 'Something happened in Plugin A',
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// Plugin B subscribes
|
|
274
|
+
useEffect(() => {
|
|
275
|
+
const unsubscribe = window.__LEGO_CHANNEL_BUS__?.subscribe(
|
|
276
|
+
'custom:plugin-a-event',
|
|
277
|
+
(data) => console.log('Received from Plugin A:', data)
|
|
278
|
+
);
|
|
279
|
+
return unsubscribe;
|
|
280
|
+
}, []);
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Option 2: Via Custom Channel**
|
|
284
|
+
- Create a custom channel name: `lego:plugins:shared`
|
|
285
|
+
- Both plugins use this channel for communication
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
### Pattern 4: Reading Kernel State
|
|
290
|
+
|
|
291
|
+
**Use Case:** Plugin needs current organization ID for data fetching
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// Create a custom hook
|
|
295
|
+
export function useCurrentOrganization() {
|
|
296
|
+
const [org, setOrg] = useState(null);
|
|
297
|
+
|
|
298
|
+
useEffect(() => {
|
|
299
|
+
const kernelState = window.__LEGO_KERNEL_STATE__;
|
|
300
|
+
const state = kernelState?.useGlobalKernelState?.getState();
|
|
301
|
+
setOrg(state?.organization || null);
|
|
302
|
+
|
|
303
|
+
// Subscribe to organization changes
|
|
304
|
+
const channelBus = window.__LEGO_CHANNEL_BUS__;
|
|
305
|
+
if (!channelBus) return;
|
|
306
|
+
|
|
307
|
+
const unsubscribe = channelBus.subscribe('lego:organization:change', (data) => {
|
|
308
|
+
const newState = kernelState?.useGlobalKernelState?.getState();
|
|
309
|
+
setOrg(newState?.organization || null);
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
return unsubscribe;
|
|
313
|
+
}, []);
|
|
314
|
+
|
|
315
|
+
return org;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Use in component
|
|
319
|
+
function MyPluginComponent() {
|
|
320
|
+
const organization = useCurrentOrganization();
|
|
321
|
+
const orgId = organization?.id;
|
|
322
|
+
|
|
323
|
+
const { data: todos } = useTodos(orgId);
|
|
324
|
+
// ...
|
|
325
|
+
}
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
---
|
|
329
|
+
|
|
330
|
+
## Type Safety
|
|
331
|
+
|
|
332
|
+
### Plugin Type Definitions
|
|
333
|
+
|
|
334
|
+
**File:** `packages/plugins/@lego/plugin-<name>/src/vite-env.d.ts`
|
|
335
|
+
|
|
336
|
+
```typescript
|
|
337
|
+
declare global {
|
|
338
|
+
interface Window {
|
|
339
|
+
__LEGO_KERNEL_STATE__?: {
|
|
340
|
+
user?: { id: string; email: string; name?: string } | null;
|
|
341
|
+
organization?: { id: string; name: string; slug: string } | null;
|
|
342
|
+
isAuthenticated: boolean;
|
|
343
|
+
useGlobalKernelState?: any;
|
|
344
|
+
};
|
|
345
|
+
__LEGO_CHANNEL_BUS__?: {
|
|
346
|
+
publish: (channel: string, message: any) => void;
|
|
347
|
+
subscribe: (channel: string, callback: (data: any) => void) => () => void;
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export {};
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## Best Practices
|
|
358
|
+
|
|
359
|
+
### DO ✅
|
|
360
|
+
|
|
361
|
+
1. **Use window bridge** for all host communication
|
|
362
|
+
2. **Subscribe to auth/org changes** to refresh data
|
|
363
|
+
3. **Publish toast notifications** for user feedback
|
|
364
|
+
4. **Read kernel state** via `__LEGO_KERNEL_STATE__`
|
|
365
|
+
5. **Clean up subscriptions** in useEffect returns
|
|
366
|
+
6. **Type window bridge** in vite-env.d.ts
|
|
367
|
+
|
|
368
|
+
### DON'T ❌
|
|
369
|
+
|
|
370
|
+
1. **Don't import from host** - Use window bridge instead
|
|
371
|
+
2. **Don't modify kernel state** - It's read-only
|
|
372
|
+
3. **Don't forget cleanup** - Always return unsubscribe function
|
|
373
|
+
4. **Don't create circular dependencies** - Host → Plugin is OK, Plugin → Host via channels
|
|
374
|
+
5. **Don't spam channels** - Use appropriate message frequency
|
|
375
|
+
|
|
376
|
+
---
|
|
377
|
+
|
|
378
|
+
## Complete Example: Plugin with Channel Integration
|
|
379
|
+
|
|
380
|
+
```typescript
|
|
381
|
+
// packages/plugins/@lego/plugin-todo/src/hooks/useChannelIntegration.ts
|
|
382
|
+
import { useEffect } from 'react';
|
|
383
|
+
|
|
384
|
+
export function useChannelIntegration() {
|
|
385
|
+
useEffect(() => {
|
|
386
|
+
const channelBus = window.__LEGO_CHANNEL_BUS__;
|
|
387
|
+
const kernelState = window.__LEGO_KERNEL_STATE__;
|
|
388
|
+
|
|
389
|
+
if (!channelBus || !kernelState) return;
|
|
390
|
+
|
|
391
|
+
// Subscribe to auth changes
|
|
392
|
+
const unsubAuth = channelBus.subscribe('lego:auth:change', (data) => {
|
|
393
|
+
if (data.isAuthenticated) {
|
|
394
|
+
console.log('[Todo Plugin] User logged in, loading todos');
|
|
395
|
+
} else {
|
|
396
|
+
console.log('[Todo Plugin] User logged out, clearing todos');
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// Subscribe to organization changes
|
|
401
|
+
const unsubOrg = channelBus.subscribe('lego:organization:change', (data) => {
|
|
402
|
+
console.log('[Todo Plugin] Organization changed:', data.organizationName);
|
|
403
|
+
// Refresh todos for new organization
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
// Notify host that plugin is ready
|
|
407
|
+
channelBus.publish('lego:plugin:ready', {
|
|
408
|
+
pluginName: '@lego/plugin-todo',
|
|
409
|
+
version: '1.0.0',
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
// Cleanup
|
|
413
|
+
return () => {
|
|
414
|
+
unsubAuth();
|
|
415
|
+
unsubOrg();
|
|
416
|
+
};
|
|
417
|
+
}, []);
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
## Channel Hooks (Host Side)
|
|
424
|
+
|
|
425
|
+
For host components, use the channel hooks:
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
428
|
+
import {
|
|
429
|
+
useChannel,
|
|
430
|
+
usePublish,
|
|
431
|
+
useToastChannel,
|
|
432
|
+
useNavigationChannel,
|
|
433
|
+
} from '@lego/kernel/channels';
|
|
434
|
+
|
|
435
|
+
// Subscribe to any channel
|
|
436
|
+
useChannel(ChannelName.AUTH_CHANGE, (data) => {
|
|
437
|
+
console.log('Auth changed:', data);
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
// Publish to toast channel
|
|
441
|
+
const publishToast = useToastChannel();
|
|
442
|
+
publishToast({ type: 'success', title: 'Done!' });
|
|
443
|
+
|
|
444
|
+
// Publish to any channel
|
|
445
|
+
const publish = usePublish();
|
|
446
|
+
publish({ channel: ChannelName.NAVIGATION, data: {...} });
|
|
447
|
+
```
|
|
448
|
+
|
|
449
|
+
---
|
|
450
|
+
|
|
451
|
+
**Next:** Read [`07-plugin-development.md`](./07-plugin-development.md) for complete plugin creation guide.
|