@gears-frontx/react 0.1.0
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/CLAUDE.md +421 -0
- package/commands/hai3-duplicate-screenset.md +58 -0
- package/commands/hai3-new-component.md +48 -0
- package/commands/hai3-new-mfe.md +166 -0
- package/commands/hai3-new-screen.md +39 -0
- package/commands/hai3-new-screenset.md +47 -0
- package/dist/index.cjs +903 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +590 -0
- package/dist/index.d.ts +590 -0
- package/dist/index.js +872 -0
- package/dist/index.js.map +1 -0
- package/dist/types-Cp8Zm5s0.d.cts +136 -0
- package/dist/types-Cp8Zm5s0.d.ts +136 -0
- package/dist/types.cjs +19 -0
- package/dist/types.cjs.map +1 -0
- package/dist/types.d.cts +3 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.js +1 -0
- package/dist/types.js.map +1 -0
- package/llms.txt +122 -0
- package/package.json +75 -0
package/CLAUDE.md
ADDED
|
@@ -0,0 +1,421 @@
|
|
|
1
|
+
# @gears-frontx/react
|
|
2
|
+
|
|
3
|
+
React bindings and hooks for HAI3 applications. Provides the React integration layer with MFE (Microfrontend) support.
|
|
4
|
+
|
|
5
|
+
## React Layer
|
|
6
|
+
|
|
7
|
+
This package is part of the **React Layer (L3)** - it depends only on @gears-frontx/framework (not directly on SDK packages) and provides React-specific components and hooks.
|
|
8
|
+
|
|
9
|
+
## Core Concepts
|
|
10
|
+
|
|
11
|
+
### HAI3Provider
|
|
12
|
+
|
|
13
|
+
Wrap your app with HAI3Provider to enable all hooks:
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { HAI3Provider } from '@gears-frontx/react';
|
|
17
|
+
|
|
18
|
+
function App() {
|
|
19
|
+
return (
|
|
20
|
+
<HAI3Provider>
|
|
21
|
+
<YourApp />
|
|
22
|
+
</HAI3Provider>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// With configuration
|
|
27
|
+
<HAI3Provider config={{ devMode: true }}>
|
|
28
|
+
<YourApp />
|
|
29
|
+
</HAI3Provider>
|
|
30
|
+
|
|
31
|
+
// With pre-built app
|
|
32
|
+
const app = createHAI3().use(screensets()).build();
|
|
33
|
+
<HAI3Provider app={app}>
|
|
34
|
+
<YourApp />
|
|
35
|
+
</HAI3Provider>
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Available Hooks
|
|
39
|
+
|
|
40
|
+
#### useHAI3
|
|
41
|
+
|
|
42
|
+
Access the HAI3 app instance:
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { useHAI3 } from '@gears-frontx/react';
|
|
46
|
+
|
|
47
|
+
function MyComponent() {
|
|
48
|
+
const app = useHAI3();
|
|
49
|
+
|
|
50
|
+
// Access MFE-enabled registry
|
|
51
|
+
const extensions = app.screensetsRegistry.getRegisteredExtensions();
|
|
52
|
+
|
|
53
|
+
// Access MFE actions
|
|
54
|
+
await app.actions.loadExtension({ extensionId: 'home' });
|
|
55
|
+
await app.actions.mountExtension({ extensionId: 'home', domainId: 'screen', container });
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
#### useAppDispatch / useAppSelector
|
|
60
|
+
|
|
61
|
+
Type-safe Redux hooks:
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { useAppDispatch, useAppSelector } from '@gears-frontx/react';
|
|
65
|
+
|
|
66
|
+
function MyComponent() {
|
|
67
|
+
const dispatch = useAppDispatch();
|
|
68
|
+
const activeScreen = useAppSelector((state) => state.layout.screen.activeScreen);
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
#### useTranslation
|
|
73
|
+
|
|
74
|
+
Access translation utilities:
|
|
75
|
+
|
|
76
|
+
```tsx
|
|
77
|
+
import { useTranslation } from '@gears-frontx/react';
|
|
78
|
+
|
|
79
|
+
function MyComponent() {
|
|
80
|
+
const { t, language, setLanguage, isRTL } = useTranslation();
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
<div dir={isRTL ? 'rtl' : 'ltr'}>
|
|
84
|
+
<h1>{t('common:title')}</h1>
|
|
85
|
+
<p>{t('common:welcome', { name: 'John' })}</p>
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
#### useScreenTranslations
|
|
92
|
+
|
|
93
|
+
Load screen-level translations:
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
import { useScreenTranslations } from '@gears-frontx/react';
|
|
97
|
+
|
|
98
|
+
const translations = {
|
|
99
|
+
en: () => import('./i18n/en.json'),
|
|
100
|
+
es: () => import('./i18n/es.json'),
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
function HomeScreen() {
|
|
104
|
+
const { isLoaded, error } = useScreenTranslations('demo', 'home', translations);
|
|
105
|
+
|
|
106
|
+
if (!isLoaded) return <Loading />;
|
|
107
|
+
if (error) return <Error error={error} />;
|
|
108
|
+
|
|
109
|
+
return <div>...</div>;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
#### useTheme
|
|
114
|
+
|
|
115
|
+
Access theme utilities:
|
|
116
|
+
|
|
117
|
+
```tsx
|
|
118
|
+
import { useTheme } from '@gears-frontx/react';
|
|
119
|
+
|
|
120
|
+
function ThemeToggle() {
|
|
121
|
+
const { currentTheme, themes, setTheme } = useTheme();
|
|
122
|
+
|
|
123
|
+
return (
|
|
124
|
+
<select value={currentTheme} onChange={(e) => setTheme(e.target.value)}>
|
|
125
|
+
{themes.map((theme) => (
|
|
126
|
+
<option key={theme.id} value={theme.id}>{theme.name}</option>
|
|
127
|
+
))}
|
|
128
|
+
</select>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### MFE Hooks
|
|
134
|
+
|
|
135
|
+
#### useMfeBridge
|
|
136
|
+
|
|
137
|
+
Access the MFE bridge for child MFEs:
|
|
138
|
+
|
|
139
|
+
```tsx
|
|
140
|
+
import { useMfeBridge } from '@gears-frontx/react';
|
|
141
|
+
import { HAI3_ACTION_LOAD_EXT, HAI3_SHARED_PROPERTY_THEME } from '@gears-frontx/react';
|
|
142
|
+
|
|
143
|
+
function MyExtension() {
|
|
144
|
+
const bridge = useMfeBridge();
|
|
145
|
+
|
|
146
|
+
// Execute actions chain on parent
|
|
147
|
+
await bridge.executeActionsChain({
|
|
148
|
+
action: { type: HAI3_ACTION_LOAD_EXT, target: 'screen', payload: { extensionId: 'other' } }
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Get shared property
|
|
152
|
+
const theme = bridge.getProperty(HAI3_SHARED_PROPERTY_THEME);
|
|
153
|
+
}
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
#### useSharedProperty
|
|
157
|
+
|
|
158
|
+
Subscribe to shared property changes:
|
|
159
|
+
|
|
160
|
+
```tsx
|
|
161
|
+
import { useSharedProperty, HAI3_SHARED_PROPERTY_THEME } from '@gears-frontx/react';
|
|
162
|
+
|
|
163
|
+
function ThemedComponent() {
|
|
164
|
+
const theme = useSharedProperty(HAI3_SHARED_PROPERTY_THEME);
|
|
165
|
+
|
|
166
|
+
return <div style={{ backgroundColor: theme?.primaryColor }}>...</div>;
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
#### useHostAction
|
|
171
|
+
|
|
172
|
+
Invoke actions on the host application:
|
|
173
|
+
|
|
174
|
+
```tsx
|
|
175
|
+
import { useHostAction, HAI3_ACTION_LOAD_EXT } from '@gears-frontx/react';
|
|
176
|
+
|
|
177
|
+
function MyExtension() {
|
|
178
|
+
const loadExtension = useHostAction(HAI3_ACTION_LOAD_EXT);
|
|
179
|
+
|
|
180
|
+
const handleClick = () => {
|
|
181
|
+
loadExtension({ extensionId: 'other' });
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
return <button onClick={handleClick}>Load Extension</button>;
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
#### useDomainExtensions
|
|
189
|
+
|
|
190
|
+
Subscribe to extensions in a domain:
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
import { useDomainExtensions } from '@gears-frontx/react';
|
|
194
|
+
|
|
195
|
+
function ScreenList() {
|
|
196
|
+
const screenExtensions = useDomainExtensions('screen');
|
|
197
|
+
|
|
198
|
+
return (
|
|
199
|
+
<ul>
|
|
200
|
+
{screenExtensions.map((ext) => (
|
|
201
|
+
<li key={ext.id}>{ext.title}</li>
|
|
202
|
+
))}
|
|
203
|
+
</ul>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
#### useRegisteredPackages
|
|
209
|
+
|
|
210
|
+
Subscribe to registered GTS packages:
|
|
211
|
+
|
|
212
|
+
```tsx
|
|
213
|
+
import { useRegisteredPackages } from '@gears-frontx/react';
|
|
214
|
+
|
|
215
|
+
function PackageList() {
|
|
216
|
+
const packages = useRegisteredPackages();
|
|
217
|
+
|
|
218
|
+
return (
|
|
219
|
+
<ul>
|
|
220
|
+
{packages.map((pkg) => (
|
|
221
|
+
<li key={pkg}>{pkg}</li>
|
|
222
|
+
))}
|
|
223
|
+
</ul>
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
#### useActivePackage
|
|
229
|
+
|
|
230
|
+
Subscribe to the active GTS package (the package of the currently mounted screen extension):
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
import { useActivePackage } from '@gears-frontx/react';
|
|
234
|
+
|
|
235
|
+
function ActivePackageIndicator() {
|
|
236
|
+
const activePackage = useActivePackage();
|
|
237
|
+
|
|
238
|
+
if (!activePackage) {
|
|
239
|
+
return <div>No active screen</div>;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return <div>Active: {activePackage}</div>;
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### MFE Components
|
|
247
|
+
|
|
248
|
+
#### MfeProvider
|
|
249
|
+
|
|
250
|
+
Provide MFE context for child extensions:
|
|
251
|
+
|
|
252
|
+
```tsx
|
|
253
|
+
import { MfeProvider } from '@gears-frontx/react';
|
|
254
|
+
|
|
255
|
+
function MfeHost() {
|
|
256
|
+
return (
|
|
257
|
+
<MfeProvider bridge={parentBridge}>
|
|
258
|
+
<ExtensionContainer />
|
|
259
|
+
</MfeProvider>
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
#### ExtensionDomainSlot
|
|
265
|
+
|
|
266
|
+
Render extensions into a domain slot:
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
import { ExtensionDomainSlot } from '@gears-frontx/react';
|
|
270
|
+
|
|
271
|
+
function LayoutScreen() {
|
|
272
|
+
return (
|
|
273
|
+
<div>
|
|
274
|
+
<ExtensionDomainSlot
|
|
275
|
+
domainId="screen"
|
|
276
|
+
containerRef={screenContainerRef}
|
|
277
|
+
fallback={<Loading />}
|
|
278
|
+
/>
|
|
279
|
+
</div>
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
#### RefContainerProvider
|
|
285
|
+
|
|
286
|
+
Provide container references for MFE mounting:
|
|
287
|
+
|
|
288
|
+
```tsx
|
|
289
|
+
import { RefContainerProvider } from '@gears-frontx/react';
|
|
290
|
+
|
|
291
|
+
function Layout() {
|
|
292
|
+
return (
|
|
293
|
+
<RefContainerProvider>
|
|
294
|
+
<ScreenContainer />
|
|
295
|
+
<SidebarContainer />
|
|
296
|
+
</RefContainerProvider>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Components
|
|
302
|
+
|
|
303
|
+
## Key Rules
|
|
304
|
+
|
|
305
|
+
1. **Wrap with HAI3Provider** - Required for all hooks to work
|
|
306
|
+
2. **Use hooks for state access** - Don't import selectors directly from @gears-frontx/framework
|
|
307
|
+
3. **Lazy load translations** - Use `useScreenTranslations` for screen-level i18n
|
|
308
|
+
4. **Use MFE hooks for extensions** - `useMfeBridge`, `useSharedProperty`, `useHostAction`, `useDomainExtensions`
|
|
309
|
+
5. **NO Layout components here** - Layout and UI components belong in L4 (user's project via CLI scaffolding)
|
|
310
|
+
|
|
311
|
+
## Re-exports
|
|
312
|
+
|
|
313
|
+
For convenience, this package re-exports everything from @gears-frontx/framework:
|
|
314
|
+
|
|
315
|
+
- All SDK primitives (eventBus, createStore, etc.)
|
|
316
|
+
- All plugins (screensets, themes, layout, microfrontends, etc.)
|
|
317
|
+
- All registries and factory functions
|
|
318
|
+
- All types (including MFE types)
|
|
319
|
+
- All MFE actions, selectors, and domain constants
|
|
320
|
+
|
|
321
|
+
This allows users to import everything from `@gears-frontx/react` without needing `@gears-frontx/framework` directly.
|
|
322
|
+
|
|
323
|
+
## Exports
|
|
324
|
+
|
|
325
|
+
### Components
|
|
326
|
+
- `HAI3Provider` - Main context provider
|
|
327
|
+
- `MfeProvider` - MFE context provider
|
|
328
|
+
- `ExtensionDomainSlot` - Domain slot renderer
|
|
329
|
+
- `RefContainerProvider` - Container reference provider
|
|
330
|
+
|
|
331
|
+
### Hooks
|
|
332
|
+
- `useHAI3` - Access app instance
|
|
333
|
+
- `useAppDispatch` - Typed dispatch
|
|
334
|
+
- `useAppSelector` - Typed selector
|
|
335
|
+
- `useTranslation` - Translation utilities
|
|
336
|
+
- `useScreenTranslations` - Screen translation loading
|
|
337
|
+
- `useTheme` - Theme utilities
|
|
338
|
+
- `useMfeBridge` - Access MFE bridge
|
|
339
|
+
- `useSharedProperty` - Subscribe to shared property
|
|
340
|
+
- `useHostAction` - Invoke host action
|
|
341
|
+
- `useDomainExtensions` - Subscribe to domain extensions
|
|
342
|
+
- `useRegisteredPackages` - Subscribe to registered GTS packages
|
|
343
|
+
- `useActivePackage` - Subscribe to active GTS package
|
|
344
|
+
|
|
345
|
+
### Context
|
|
346
|
+
- `HAI3Context` - React context (for advanced use)
|
|
347
|
+
- `MfeContext` - MFE context (for advanced use)
|
|
348
|
+
|
|
349
|
+
### Types
|
|
350
|
+
- `HAI3ProviderProps`
|
|
351
|
+
- `MfeProviderProps`, `ExtensionDomainSlotProps`
|
|
352
|
+
- `UseTranslationReturn`, `UseThemeReturn`
|
|
353
|
+
- All types from @gears-frontx/framework
|
|
354
|
+
|
|
355
|
+
## Migration from Legacy API
|
|
356
|
+
|
|
357
|
+
The `useNavigation` hook has been removed. Use MFE hooks and actions instead:
|
|
358
|
+
|
|
359
|
+
### Removed Hook
|
|
360
|
+
- `useNavigation()` (replaced by MFE actions and hooks)
|
|
361
|
+
|
|
362
|
+
### Migration Examples
|
|
363
|
+
|
|
364
|
+
**OLD**: Navigate using hook
|
|
365
|
+
```tsx
|
|
366
|
+
import { useNavigation } from '@gears-frontx/react';
|
|
367
|
+
|
|
368
|
+
function MyComponent() {
|
|
369
|
+
const { navigateToScreen } = useNavigation();
|
|
370
|
+
|
|
371
|
+
return (
|
|
372
|
+
<button onClick={() => navigateToScreen('demo', 'home')}>
|
|
373
|
+
Go Home
|
|
374
|
+
</button>
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
**NEW**: Mount extension using app actions
|
|
380
|
+
```tsx
|
|
381
|
+
import { useHAI3 } from '@gears-frontx/react';
|
|
382
|
+
|
|
383
|
+
function MyComponent() {
|
|
384
|
+
const app = useHAI3();
|
|
385
|
+
const containerRef = useRef<HTMLDivElement>(null);
|
|
386
|
+
|
|
387
|
+
const handleNavigate = async () => {
|
|
388
|
+
if (containerRef.current) {
|
|
389
|
+
await app.actions.mountExtension({
|
|
390
|
+
extensionId: 'home',
|
|
391
|
+
domainId: 'screen',
|
|
392
|
+
container: containerRef.current,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
return (
|
|
398
|
+
<>
|
|
399
|
+
<button onClick={handleNavigate}>Go Home</button>
|
|
400
|
+
<div ref={containerRef} />
|
|
401
|
+
</>
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
**NEW (Alternative)**: Use ExtensionDomainSlot
|
|
407
|
+
```tsx
|
|
408
|
+
import { ExtensionDomainSlot } from '@gears-frontx/react';
|
|
409
|
+
|
|
410
|
+
function MyComponent() {
|
|
411
|
+
return (
|
|
412
|
+
<ExtensionDomainSlot
|
|
413
|
+
domainId="screen"
|
|
414
|
+
containerRef={screenContainerRef}
|
|
415
|
+
fallback={<Loading />}
|
|
416
|
+
/>
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
See the MFE migration guide in the project documentation for detailed migration steps.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
<!-- @standalone -->
|
|
2
|
+
# hai3:duplicate-screenset - Duplicate Screenset
|
|
3
|
+
|
|
4
|
+
## AI WORKFLOW (REQUIRED)
|
|
5
|
+
1) Read .ai/targets/SCREENSETS.md before starting.
|
|
6
|
+
2) Gather requirements from user.
|
|
7
|
+
3) Follow steps below.
|
|
8
|
+
|
|
9
|
+
REQUIRED: Copy folder and update ids.ts only (2 steps, 96% reduction).
|
|
10
|
+
REQUIRED: Pass arch:check with zero errors.
|
|
11
|
+
REQUIRED: Test via Chrome DevTools MCP (never skip).
|
|
12
|
+
|
|
13
|
+
## GATHER REQUIREMENTS
|
|
14
|
+
Ask user for:
|
|
15
|
+
- SOURCE screenset name (existing screenset to copy).
|
|
16
|
+
- TARGET screenset name (camelCase, single word preferred).
|
|
17
|
+
- TARGET category: Drafts | Mockups | Production.
|
|
18
|
+
|
|
19
|
+
## STEP 1: Copy Screenset via CLI
|
|
20
|
+
```bash
|
|
21
|
+
hai3 screenset copy SOURCE TARGET --category={category}
|
|
22
|
+
```
|
|
23
|
+
The CLI automatically:
|
|
24
|
+
- Copies all files (screens, slices, actions, events, effects, API, icons, i18n).
|
|
25
|
+
- Updates ids.ts with TARGET_SCREENSET_ID and all SCREEN_IDs.
|
|
26
|
+
- Everything auto-updates via template literals:
|
|
27
|
+
- Event enums: ${TARGET_SCREENSET_ID}/${DOMAIN_ID}/eventName.
|
|
28
|
+
- Icon IDs: ${TARGET_SCREENSET_ID}:iconName.
|
|
29
|
+
- API domain: ${TARGET_SCREENSET_ID}:serviceName.
|
|
30
|
+
- Redux state key.
|
|
31
|
+
- Screenset auto-discovered (no manual registration).
|
|
32
|
+
|
|
33
|
+
## STEP 2: Validate
|
|
34
|
+
```bash
|
|
35
|
+
npm run type-check
|
|
36
|
+
npm run arch:check
|
|
37
|
+
npm run lint
|
|
38
|
+
grep -rn "OLD_SCREENSET_ID" src/screensets/TARGET/ # Must return 0 matches
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## STEP 3: Test via Chrome DevTools MCP
|
|
42
|
+
STOP: If MCP connection is broken, fix it first. NEVER skip testing.
|
|
43
|
+
- npm run dev.
|
|
44
|
+
- Verify TARGET in screenset selector.
|
|
45
|
+
- Click TARGET screenset in dev panel.
|
|
46
|
+
- Verify URL changes to target screenset.
|
|
47
|
+
- Check 0 console errors.
|
|
48
|
+
- Test all primary features.
|
|
49
|
+
- Verify auto-discovery worked (screenset appears without manual registration).
|
|
50
|
+
|
|
51
|
+
## CHECKLIST
|
|
52
|
+
- [ ] Gather requirements (source, target name, category).
|
|
53
|
+
- [ ] Run `hai3 screenset copy SOURCE TARGET --category={category}`.
|
|
54
|
+
- [ ] Run type-check (MUST pass).
|
|
55
|
+
- [ ] Run arch:check (MUST pass).
|
|
56
|
+
- [ ] Run lint (MUST pass).
|
|
57
|
+
- [ ] Verify zero occurrences of old screenset ID.
|
|
58
|
+
- [ ] Test via Chrome DevTools MCP (NEVER skip).
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<!-- @standalone -->
|
|
2
|
+
# hai3:new-component - Add New UI Component
|
|
3
|
+
|
|
4
|
+
## AI WORKFLOW (REQUIRED)
|
|
5
|
+
1) Check for existing equivalent in project (e.g. components/ui/) and configured UI library first.
|
|
6
|
+
2) Gather requirements from user.
|
|
7
|
+
3) Implement.
|
|
8
|
+
|
|
9
|
+
## CHECK EXISTING COMPONENTS FIRST
|
|
10
|
+
- REQUIRED: Read `frontx.config.json` to find the `uikit` value.
|
|
11
|
+
- If uikit is a third-party package (not `shadcn` or `none`): read its exports from `node_modules/<package>/` to check for existing components.
|
|
12
|
+
- REQUIRED: Before creating a new component, scan the project AND the configured UI library for existing equivalents.
|
|
13
|
+
- REQUIRED: Reuse existing components if equivalent exists.
|
|
14
|
+
|
|
15
|
+
## GATHER REQUIREMENTS
|
|
16
|
+
Ask user for:
|
|
17
|
+
- Component name (e.g., "DataTable", "ColorPicker").
|
|
18
|
+
- Component description and props.
|
|
19
|
+
|
|
20
|
+
## IF SCREENSET COMPONENT
|
|
21
|
+
|
|
22
|
+
### STEP 0: Determine Subfolder
|
|
23
|
+
- components/ui/: Base UI primitives (shadcn components or custom).
|
|
24
|
+
- screens/{screen}/components/: Screen-specific components.
|
|
25
|
+
- components/: Shared composites used across screens.
|
|
26
|
+
|
|
27
|
+
### STEP 1: Implementation
|
|
28
|
+
|
|
29
|
+
#### 1.1 Create Component
|
|
30
|
+
File: src/components/ui/{ComponentName}.tsx (base primitives)
|
|
31
|
+
or: src/screens/{screen}/components/{ComponentName}.tsx (screen-specific)
|
|
32
|
+
or: src/components/{ComponentName}.tsx (shared composites)
|
|
33
|
+
- Must be reusable within the MFE.
|
|
34
|
+
- NO Redux or state management.
|
|
35
|
+
- Accept value/onChange pattern for state.
|
|
36
|
+
|
|
37
|
+
#### 1.2 Export
|
|
38
|
+
Export from local index if needed.
|
|
39
|
+
|
|
40
|
+
#### 1.3 Validation
|
|
41
|
+
Run: npm run arch:check && npm run dev
|
|
42
|
+
Test component in UI.
|
|
43
|
+
|
|
44
|
+
## RULES
|
|
45
|
+
- REQUIRED: Check existing project UI components first; create new only if missing.
|
|
46
|
+
- FORBIDDEN: Redux, business logic, side effects in components.
|
|
47
|
+
- FORBIDDEN: Inline styles outside components/ui/.
|
|
48
|
+
- REQUIRED: Accept value/onChange pattern for state.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
<!-- @standalone -->
|
|
2
|
+
# hai3:new-mfe - Create New Microfrontend Package
|
|
3
|
+
|
|
4
|
+
## PREREQUISITES (CRITICAL - STOP IF FAILED)
|
|
5
|
+
|
|
6
|
+
1. MFE must be associated with a screenset
|
|
7
|
+
2. Screenset must exist (run `hai3-new-screenset` first if needed)
|
|
8
|
+
3. Vite and Module Federation must be configured
|
|
9
|
+
|
|
10
|
+
## QUICK START
|
|
11
|
+
|
|
12
|
+
For a screenset named `{screensetName}` with a new MFE:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# 1. Create the MFE package in src/mfe_packages/{screensetName}-mfe/
|
|
16
|
+
cp -r packages/cli/template-sources/mfe-package/ src/mfe_packages/{screensetName}-mfe/
|
|
17
|
+
|
|
18
|
+
# 2. Update variables in package.json and vite.config.ts:
|
|
19
|
+
# - Replace {{mfeName}} with your screenset name (camelCase)
|
|
20
|
+
# - Replace {{port}} with available port (3001, 3010, 3020, etc)
|
|
21
|
+
# - IMPORTANT: set preview script to "vite preview --port {{port}}"
|
|
22
|
+
|
|
23
|
+
# 3. Install dependencies
|
|
24
|
+
cd src/mfe_packages/{screensetName}-mfe
|
|
25
|
+
npm install
|
|
26
|
+
|
|
27
|
+
# 4. Create src/lifecycle.tsx that extends ThemeAwareReactLifecycle
|
|
28
|
+
|
|
29
|
+
# 5. Regenerate MFE manifests so bootstrap.ts picks up the new package
|
|
30
|
+
npm run generate:mfe-manifests
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## CONFIGURATION REQUIREMENTS
|
|
34
|
+
|
|
35
|
+
✅ **Correct preview script** (required for port auto-discovery):
|
|
36
|
+
```json
|
|
37
|
+
"preview": "vite preview --port {{port}}"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
✅ **Correct dev script** (for hot reload):
|
|
41
|
+
```json
|
|
42
|
+
"dev": "vite --port {{port}}"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
❌ **WRONG** (don't use this for dev):
|
|
46
|
+
```json
|
|
47
|
+
"dev": "vite build && vite preview --port {{port}}"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## PORT ALLOCATION
|
|
51
|
+
|
|
52
|
+
Reserve ports for MFE packages:
|
|
53
|
+
- **3001**: demo-mfe
|
|
54
|
+
- **3010+**: (next available: 3010, 3020, 3030, ...)
|
|
55
|
+
- **5173**: Main app
|
|
56
|
+
|
|
57
|
+
## LIFECYCLE TEMPLATE
|
|
58
|
+
|
|
59
|
+
Create `src/lifecycle.tsx`:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import React from 'react';
|
|
63
|
+
import type { ChildMfeBridge } from '@gears-frontx/react';
|
|
64
|
+
import { ThemeAwareReactLifecycle } from '@gears-frontx/screensets/mfe/handler';
|
|
65
|
+
import { YourScreen } from './screens/YourScreen';
|
|
66
|
+
|
|
67
|
+
class Lifecycle extends ThemeAwareReactLifecycle {
|
|
68
|
+
constructor() {
|
|
69
|
+
super({
|
|
70
|
+
name: '{screensetName}',
|
|
71
|
+
version: '1.0.0',
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async mount(shadowRoot: ShadowRoot, bridge: ChildMfeBridge) {
|
|
76
|
+
const div = document.createElement('div');
|
|
77
|
+
shadowRoot.appendChild(div);
|
|
78
|
+
|
|
79
|
+
const style = document.createElement('style');
|
|
80
|
+
style.textContent = tailwindStyles;
|
|
81
|
+
shadowRoot.appendChild(style);
|
|
82
|
+
|
|
83
|
+
// Render component
|
|
84
|
+
const root = ReactDOM.createRoot(div);
|
|
85
|
+
root.render(<YourScreen bridge={bridge} />);
|
|
86
|
+
|
|
87
|
+
return { root, div };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export default new Lifecycle();
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## dev:all INTEGRATION (AUTOMATIC)
|
|
95
|
+
|
|
96
|
+
After creating the MFE and running `npm run generate:mfe-manifests`, the `dev:all`
|
|
97
|
+
script auto-discovers your new package — no manual configuration needed.
|
|
98
|
+
|
|
99
|
+
`dev-all.ts` scans `src/mfe_packages/*/package.json` and reads the port from the
|
|
100
|
+
`preview` (or `dev`) script using `--port NNNN`. The new MFE will automatically
|
|
101
|
+
appear in the next `npm run dev:all` run.
|
|
102
|
+
|
|
103
|
+
## VALIDATION
|
|
104
|
+
|
|
105
|
+
After creation:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# Regenerate manifests (required after adding new MFE)
|
|
109
|
+
npm run generate:mfe-manifests
|
|
110
|
+
|
|
111
|
+
# Validate TypeScript
|
|
112
|
+
npm run type-check
|
|
113
|
+
|
|
114
|
+
# Check architecture compliance
|
|
115
|
+
npm run arch:check
|
|
116
|
+
|
|
117
|
+
# Run the dev server (picks up new MFE automatically)
|
|
118
|
+
npm run dev:all
|
|
119
|
+
|
|
120
|
+
# Verify MFE loads at http://localhost:5173
|
|
121
|
+
# Open Studio Overlay (Ctrl+`) and check screenset
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## TROUBLESHOOTING
|
|
125
|
+
|
|
126
|
+
### Issue: MFE not appearing in screenset selector
|
|
127
|
+
- Run `npm run generate:mfe-manifests` to regenerate manifests
|
|
128
|
+
- Check mfe.json manifest is properly exported
|
|
129
|
+
- Verify extensions are registered in manifest
|
|
130
|
+
- Check browser console for errors
|
|
131
|
+
|
|
132
|
+
### Issue: dev:all not picking up MFE port
|
|
133
|
+
- Verify `preview` script has `--port NNNN` (e.g., `"vite preview --port 3010"`)
|
|
134
|
+
- Check port is not in use (`lsof -i :3010`)
|
|
135
|
+
- Restart dev server
|
|
136
|
+
|
|
137
|
+
### Issue: Hot reload not working
|
|
138
|
+
- Verify dev script uses `vite --port` (not `vite build && vite preview`)
|
|
139
|
+
- Check port is not in use
|
|
140
|
+
- Restart dev server
|
|
141
|
+
|
|
142
|
+
### Issue: Redux/useSelector errors
|
|
143
|
+
- MFE must use mock data (no Redux Provider in isolation)
|
|
144
|
+
- Use `useState` for local state management
|
|
145
|
+
- Do not import Redux hooks (@gears-frontx/react)
|
|
146
|
+
|
|
147
|
+
## BEST PRACTICES
|
|
148
|
+
|
|
149
|
+
✅ **DO:**
|
|
150
|
+
- Set `preview` script with `--port NNNN` for port auto-discovery
|
|
151
|
+
- Use mock data with useState for UI-only MFEs
|
|
152
|
+
- Keep MFE logic isolated and simple
|
|
153
|
+
- Use local components (e.g. components/ui/)
|
|
154
|
+
- Run `npm run generate:mfe-manifests` after creating the MFE
|
|
155
|
+
|
|
156
|
+
❌ **DON'T:**
|
|
157
|
+
- Import Redux hooks directly
|
|
158
|
+
- Use vite build && vite preview in dev mode
|
|
159
|
+
- Create complex state management in MFE
|
|
160
|
+
- Hardcode ports without updating package.json preview script
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
See also:
|
|
165
|
+
- `/packages/cli/template-sources/mfe-package/` for templates
|
|
166
|
+
- `hai3-new-screenset.md` for screenset creation
|