@myop/cli 0.1.45 → 0.1.46

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.
@@ -0,0 +1,265 @@
1
+ ---
2
+ name: myop-react-host
3
+ description: "Integrate Myop components into React applications using @myop/react. This skill covers MyopComponent props, typed event handlers, data binding, preloading, auto-generated packages, and local dev setup. Activate when the user is building a React app that hosts Myop components, or when you see @myop/react in package.json."
4
+ ---
5
+
6
+ # Myop React Host Integration
7
+
8
+ Embed Myop components in React applications using `@myop/react`.
9
+
10
+ ## When This Skill Activates
11
+
12
+ - `@myop/react` is in `package.json` dependencies
13
+ - User asks to "add a Myop component to React", "integrate Myop", "use MyopComponent"
14
+ - Files import from `@myop/react`
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @myop/react @myop/sdk
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ### Option 1: Auto-Generated Package (Recommended)
25
+
26
+ Every Myop component has an auto-generated, fully typed React package:
27
+
28
+ ```bash
29
+ npm install https://cloud.myop.dev/npm/{componentId}/react
30
+ ```
31
+
32
+ ```tsx
33
+ import { MyComponent } from "@myop/my-component";
34
+
35
+ function App() {
36
+ return (
37
+ <MyComponent
38
+ data={{ title: "Hello", items: ["a", "b"] }}
39
+ onItemSelected={(payload) => {
40
+ // payload is typed: { itemId: string }
41
+ console.log(payload.itemId);
42
+ }}
43
+ />
44
+ );
45
+ }
46
+ ```
47
+
48
+ **Why auto-generated packages:**
49
+ - Full TypeScript types for `data` and all CTA events — auto-generated from the component's `<script type="myop/types">` block
50
+ - No `componentId` prop needed — baked into the package
51
+ - Tiny bundle impact (~6KB for `@myop/react`, component loads at runtime)
52
+
53
+ ### Option 2: MyopComponent Directly
54
+
55
+ ```tsx
56
+ import { MyopComponent } from "@myop/react";
57
+
58
+ function App() {
59
+ return (
60
+ <MyopComponent
61
+ componentId="your-component-id"
62
+ data={{ title: "Hello" }}
63
+ on={(action, payload) => {
64
+ console.log(action, payload);
65
+ }}
66
+ />
67
+ );
68
+ }
69
+ ```
70
+
71
+ ## MyopComponent Props
72
+
73
+ | Prop | Type | Default | Description |
74
+ |------|------|---------|-------------|
75
+ | `componentId` | `string` | — | Myop component ID (UUID) |
76
+ | `data` | `TData` | — | Data passed to `myop_init_interface`. Reactive — updates trigger re-render in component |
77
+ | `on` | `(action, payload) => void` | — | Generic handler for all CTA events |
78
+ | `on[ActionName]` | `(payload) => void` | — | Typed handler for specific CTA (e.g., `onItemSelected`) |
79
+ | `onLoad` | `(component) => void` | — | Called when component finishes loading |
80
+ | `onError` | `(error: string) => void` | — | Called on load failure |
81
+ | `onSizeChange` | `(size) => boolean \| void` | — | Called when auto-sized component changes dimensions. Return `false` to reject |
82
+ | `style` | `CSSProperties` | — | CSS styles for the outer container |
83
+ | `loader` | `ReactNode` | — | Custom loading indicator (shown while component loads) |
84
+ | `fallback` | `ReactNode` | — | Custom error fallback UI |
85
+ | `fadeDuration` | `number` | `200` | Loader fade-out duration in ms |
86
+ | `autoSize` | `boolean` | `false` | Auto-size container to match iframe content |
87
+ | `environment` | `string` | — | Load from specific environment (e.g., `"staging"`) |
88
+ | `preview` | `boolean` | `false` | Load unpublished preview version |
89
+
90
+ ## Typed Event Handlers
91
+
92
+ CTA actions from the component are converted to `on[PascalCase]` props:
93
+
94
+ | Component CTA action | React prop |
95
+ |----------------------|------------|
96
+ | `'item-selected'` | `onItemSelected` |
97
+ | `'form-submitted'` | `onFormSubmitted` |
98
+ | `'row-clicked'` | `onRowClicked` |
99
+
100
+ ```tsx
101
+ // Both work — use typed handlers for type safety, `on` for catch-all
102
+ <MyopComponent<MyData, MyCtaPayloads>
103
+ componentId="..."
104
+ data={data}
105
+ onItemSelected={(payload) => {
106
+ // payload typed as MyCtaPayloads['item-selected']
107
+ }}
108
+ on={(action, payload) => {
109
+ // Generic catch-all
110
+ }}
111
+ />
112
+ ```
113
+
114
+ ## Data Binding
115
+
116
+ The `data` prop is reactive. When it changes, `myop_init_interface(data)` is called automatically:
117
+
118
+ ```tsx
119
+ function App() {
120
+ const [count, setCount] = useState(0);
121
+
122
+ return (
123
+ <>
124
+ <button onClick={() => setCount(c => c + 1)}>+1</button>
125
+ <MyopComponent
126
+ componentId="counter-display"
127
+ data={{ count }} // Component updates when count changes
128
+ />
129
+ </>
130
+ );
131
+ }
132
+ ```
133
+
134
+ ## Preloading
135
+
136
+ Preload components to eliminate loading delay when they mount:
137
+
138
+ ```tsx
139
+ import { preloadComponents, isPreloaded } from "@myop/react";
140
+
141
+ // Preload on app startup or route entry
142
+ await preloadComponents(["component-id-1", "component-id-2"]);
143
+
144
+ // Optionally preload for a specific environment
145
+ await preloadComponents(["component-id-1"], "staging");
146
+
147
+ // Check if a component is cached
148
+ if (isPreloaded("component-id-1")) {
149
+ console.log("Will render instantly");
150
+ }
151
+ ```
152
+
153
+ **When to preload:**
154
+ - App startup — preload components on the landing page
155
+ - Route transitions — preload components for the next page
156
+ - Tab containers — preload all tab contents when container mounts
157
+
158
+ ## Configuration Functions
159
+
160
+ ```tsx
161
+ import {
162
+ enableLocalDev,
163
+ setCloudRepositoryUrl,
164
+ setEnvironment,
165
+ } from "@myop/react";
166
+
167
+ // Point to local dev server (myop dev on port 9292)
168
+ enableLocalDev();
169
+
170
+ // Custom cloud URL
171
+ setCloudRepositoryUrl("https://custom.myop.dev");
172
+
173
+ // Set default environment for all components
174
+ setEnvironment("staging");
175
+ ```
176
+
177
+ ## Accessing the Component Instance
178
+
179
+ Use `onLoad` to get direct access to the loaded component:
180
+
181
+ ```tsx
182
+ <MyopComponent
183
+ componentId="..."
184
+ onLoad={(component) => {
185
+ // component.props.myop_init_interface(data) — update data
186
+ // component.props.myop_cta_handler — read current handler
187
+ // component.dispose() — cleanup
188
+ // component.hide() / component.show()
189
+ }}
190
+ />
191
+ ```
192
+
193
+ ## Auto-Size Mode
194
+
195
+ When `autoSize={true}`, the container dimensions follow the iframe content:
196
+
197
+ ```tsx
198
+ <MyopComponent
199
+ componentId="..."
200
+ autoSize={true}
201
+ onSizeChange={(size) => {
202
+ console.log(size.width, size.height);
203
+ // Return false to reject the size change
204
+ }}
205
+ style={{ maxWidth: 600, maxHeight: 400 }}
206
+ />
207
+ ```
208
+
209
+ ## Complete Example
210
+
211
+ ```tsx
212
+ import { MyopComponent, preloadComponents } from "@myop/react";
213
+ import { useEffect, useState } from "react";
214
+
215
+ // Preload on module load
216
+ preloadComponents(["sidebar-abc123", "chart-def456"]);
217
+
218
+ function Dashboard() {
219
+ const [tasks, setTasks] = useState([
220
+ { id: "1", title: "Review PR", completed: false },
221
+ { id: "2", title: "Deploy", completed: true },
222
+ ]);
223
+
224
+ return (
225
+ <div style={{ display: "flex", height: "100vh" }}>
226
+ <MyopComponent
227
+ componentId="sidebar-abc123"
228
+ data={{ tasks }}
229
+ onTaskToggled={({ taskId, completed }) => {
230
+ setTasks(prev =>
231
+ prev.map(t => t.id === taskId ? { ...t, completed } : t)
232
+ );
233
+ }}
234
+ onTaskDeleted={({ taskId }) => {
235
+ setTasks(prev => prev.filter(t => t.id !== taskId));
236
+ }}
237
+ loader={<div>Loading sidebar...</div>}
238
+ fallback={<div>Failed to load sidebar</div>}
239
+ style={{ width: 300, height: "100%" }}
240
+ />
241
+ <MyopComponent
242
+ componentId="chart-def456"
243
+ data={{ tasks }}
244
+ style={{ flex: 1 }}
245
+ />
246
+ </div>
247
+ );
248
+ }
249
+ ```
250
+
251
+ ## TypeScript Types
252
+
253
+ ```typescript
254
+ import type {
255
+ IPropTypes, // Full props type (base + event handlers)
256
+ ITypedMyopComponent, // Component instance with typed props
257
+ IMyopComponentProps, // Props interface (myop_init_interface + myop_cta_handler)
258
+ EventHandlerProps, // Generated on[Action] props type
259
+ KebabToPascal, // Utility: 'item-selected' -> 'ItemSelected'
260
+ } from "@myop/react";
261
+ ```
262
+
263
+ ## Reference
264
+
265
+ - [Auto-Generated Packages](references/auto-generated-packages.md)
@@ -0,0 +1,70 @@
1
+ # Auto-Generated Packages
2
+
3
+ Myop auto-generates a fully typed npm package for every component, for each framework.
4
+
5
+ ## Install URL Pattern
6
+
7
+ ```
8
+ https://cloud.myop.dev/npm/{componentId}/react
9
+ https://cloud.myop.dev/npm/{componentId}/vue
10
+ https://cloud.myop.dev/npm/{componentId}/angular
11
+ https://cloud.myop.dev/npm/{componentId}/react-native
12
+ https://cloud.myop.dev/npm/{componentId}/typescript
13
+ ```
14
+
15
+ ## How It Works
16
+
17
+ 1. The server reads the component's `<script type="myop/types">` block
18
+ 2. Extracts `MyopInitData` and `MyopCtaPayloads` interfaces
19
+ 3. Generates a typed wrapper component with the `componentId` baked in
20
+ 4. Returns a `.tgz` package installable via `npm install <url>`
21
+
22
+ ## What Gets Generated (React)
23
+
24
+ ```
25
+ package/
26
+ package.json # @myop/{component-name}, peerDeps: react, @myop/react
27
+ index.tsx # forwardRef component wrapping MyopComponent
28
+ index.d.ts # TypeScript declarations
29
+ index.js # Compiled CommonJS
30
+ ```
31
+
32
+ ### Generated Component Code
33
+
34
+ ```tsx
35
+ import { MyopComponent, IPropTypes } from '@myop/react';
36
+ import { forwardRef } from 'react';
37
+
38
+ // Types extracted from the component's <script type="myop/types">
39
+ export interface MyopInitData { /* ... */ }
40
+ export interface MyopCtaPayloads { /* ... */ }
41
+
42
+ export type MyComponentProps = Omit<IPropTypes<MyopInitData, MyopCtaPayloads>, 'componentId'>;
43
+
44
+ export const MyComponent = forwardRef<any, MyComponentProps>((props, ref) => (
45
+ <MyopComponent<MyopInitData, MyopCtaPayloads>
46
+ {...props}
47
+ componentId="baked-in-uuid"
48
+ ref={ref}
49
+ />
50
+ ));
51
+ ```
52
+
53
+ ### What You Get
54
+
55
+ - `data` prop is typed as `MyopInitData`
56
+ - Individual CTA handlers are typed (e.g., `onItemSelected: (payload: { itemId: string }) => void`)
57
+ - No need to pass `componentId` — it's baked in
58
+ - Full IntelliSense / autocomplete in IDEs
59
+
60
+ ## Info Endpoint
61
+
62
+ Get install instructions and component info:
63
+
64
+ ```
65
+ https://cloud.myop.dev/npm/{componentId}/react/info
66
+ ```
67
+
68
+ ## Updating
69
+
70
+ Re-run `npm install <url>` to get the latest types after updating the component's type definitions.
@@ -0,0 +1,320 @@
1
+ ---
2
+ name: myop-react-native-host
3
+ description: "Integrate Myop components into React Native applications using @myop/react-native. This skill covers MyopComponent props, CTA event handling, data binding, preloading, auto-generated packages, native-specific features (scroll, zoom, selection control), and local dev setup. Activate when the user is building a React Native app that hosts Myop components, or when you see @myop/react-native in package.json."
4
+ ---
5
+
6
+ # Myop React Native Host Integration
7
+
8
+ Embed Myop components in React Native applications using `@myop/react-native`. Components render inside a WebView with a native bridge for CTA communication.
9
+
10
+ ## When This Skill Activates
11
+
12
+ - `@myop/react-native` is in `package.json` dependencies
13
+ - User asks to "add a Myop component to React Native", "integrate Myop in mobile"
14
+ - Files import from `@myop/react-native`
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @myop/react-native react-native-webview
20
+ ```
21
+
22
+ Peer dependencies: `react >= 16.8.0`, `react-native >= 0.60.0`, `react-native-webview >= 11.0.0`.
23
+
24
+ For Expo projects:
25
+ ```bash
26
+ npx expo install react-native-webview
27
+ npm install @myop/react-native
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ### Option 1: Auto-Generated Package (Recommended)
33
+
34
+ ```bash
35
+ npm install https://cloud.myop.dev/npm/{componentId}/react-native
36
+ ```
37
+
38
+ ```tsx
39
+ import { MyComponent } from "@myop/my-component";
40
+
41
+ function App() {
42
+ return (
43
+ <MyComponent
44
+ data={{ title: "Hello", items: ["a", "b"] }}
45
+ onItemSelected={(payload) => {
46
+ console.log(payload.itemId);
47
+ }}
48
+ style={{ flex: 1 }}
49
+ />
50
+ );
51
+ }
52
+ ```
53
+
54
+ The auto-generated package bakes in the `componentId` and exports typed interfaces.
55
+
56
+ ### Option 2: MyopComponent Directly
57
+
58
+ ```tsx
59
+ import { MyopComponent } from "@myop/react-native";
60
+
61
+ function App() {
62
+ return (
63
+ <MyopComponent
64
+ componentId="your-component-id"
65
+ data={{ title: "Hello" }}
66
+ on={(action, payload) => {
67
+ console.log(action, payload);
68
+ }}
69
+ style={{ flex: 1 }}
70
+ />
71
+ );
72
+ }
73
+ ```
74
+
75
+ ## MyopComponent Props
76
+
77
+ | Prop | Type | Default | Description |
78
+ |------|------|---------|-------------|
79
+ | `componentId` | `string` | — | Myop component ID (UUID) |
80
+ | `data` | `any` | — | Data passed to `myop_init_interface`. Reactive — updates trigger re-render |
81
+ | `on` | `(action, payload) => void` | — | Generic handler for all CTA events |
82
+ | `onLoad` | `(component: IMyopComponentProxy) => void` | — | Called when component finishes loading |
83
+ | `onError` | `(error: string) => void` | — | Called on load failure |
84
+ | `style` | `StyleProp<ViewStyle>` | `{width:'100%', height:'100%'}` | Style for the outer View container |
85
+ | `loader` | `ReactNode` | — | Custom loading indicator |
86
+ | `fallback` | `ReactNode` | — | Custom error fallback UI |
87
+ | `fadeDuration` | `number` | `200` | Loader fade-out duration in ms |
88
+ | `environment` | `string` | — | Load from specific environment |
89
+ | `preview` | `boolean` | `false` | Load unpublished preview version |
90
+ | `scrollEnabled` | `boolean` | `false` | Allow scrolling inside the WebView |
91
+ | `zoomEnabled` | `boolean` | `false` | Allow pinch-to-zoom |
92
+ | `selectionEnabled` | `boolean` | `false` | Allow text selection (disabled for native feel) |
93
+ | `componentConfig` | `IComponentInstanceConfig` | — | Direct config object (advanced) |
94
+
95
+ ### Native-Specific Props
96
+
97
+ The `scrollEnabled`, `zoomEnabled`, and `selectionEnabled` props are unique to React Native:
98
+
99
+ ```tsx
100
+ <MyopComponent
101
+ componentId="article-viewer"
102
+ data={{ article }}
103
+ scrollEnabled={true} // Allow scrolling for long content
104
+ zoomEnabled={false} // Disable pinch zoom
105
+ selectionEnabled={false} // Disable text selection for native feel
106
+ style={{ flex: 1 }}
107
+ />
108
+ ```
109
+
110
+ When `selectionEnabled={false}` (default), CSS rules are injected to prevent text selection and touch callout, with exceptions for `<input>`, `<textarea>`, and `[contenteditable]` elements.
111
+
112
+ ## CTA Event Handling
113
+
114
+ CTA events from the component are received via `postMessage` bridge and dispatched to the `on` callback:
115
+
116
+ ```tsx
117
+ <MyopComponent
118
+ componentId="task-list"
119
+ data={{ tasks }}
120
+ on={(action, payload) => {
121
+ switch (action) {
122
+ case 'task-toggled':
123
+ console.log(payload.taskId, payload.completed);
124
+ break;
125
+ case 'task-deleted':
126
+ console.log(payload.taskId);
127
+ break;
128
+ }
129
+ }}
130
+ />
131
+ ```
132
+
133
+ ## Data Binding
134
+
135
+ The `data` prop is reactive. When it changes (compared by JSON serialization to avoid redundant calls), `myop_init_interface(data)` is called automatically:
136
+
137
+ ```tsx
138
+ function Counter() {
139
+ const [count, setCount] = useState(0);
140
+
141
+ return (
142
+ <View>
143
+ <Button title="+1" onPress={() => setCount(c => c + 1)} />
144
+ <MyopComponent
145
+ componentId="counter-display"
146
+ data={{ count }}
147
+ style={{ height: 200 }}
148
+ />
149
+ </View>
150
+ );
151
+ }
152
+ ```
153
+
154
+ **Note:** When `data` is provided, the WebView is hidden (opacity: 0) until the component has loaded and real data has been applied. This prevents the component's preview/placeholder content from flashing before `myop_init_interface` is called.
155
+
156
+ ## Component Instance (IMyopComponentProxy)
157
+
158
+ The `onLoad` callback receives a proxy object for controlling the component:
159
+
160
+ ```tsx
161
+ <MyopComponent
162
+ componentId="..."
163
+ onLoad={(component) => {
164
+ // component.id — the component ID
165
+ // component.props.myop_init_interface(data) — update data
166
+ // component.element.style.set('background', 'red') — style DOM
167
+ // component.element.style.get('background') — returns Promise
168
+ // component.element.set('innerHTML', '<b>hi</b>') — set DOM property
169
+ // component.element.get('scrollHeight') — returns Promise
170
+ // component.dispose() — cleanup
171
+ // component.hide() / component.show()
172
+ }}
173
+ />
174
+ ```
175
+
176
+ The proxy communicates with the WebView via `injectJavaScript`. Element `get()` operations are asynchronous (return Promises) because they cross the native-WebView bridge.
177
+
178
+ ## Preloading
179
+
180
+ ```tsx
181
+ import { preloadComponents, isPreloaded } from "@myop/react-native";
182
+
183
+ // Preload on app startup or before navigating to a screen
184
+ await preloadComponents(["component-id-1", "component-id-2"]);
185
+ await preloadComponents(["component-id-1"], "staging");
186
+
187
+ if (isPreloaded("component-id-1")) {
188
+ console.log("Will render instantly — no loader shown");
189
+ }
190
+ ```
191
+
192
+ When a component is preloaded, the loader is not shown at all.
193
+
194
+ ## Configuration Functions
195
+
196
+ ```tsx
197
+ import {
198
+ enableLocalDev,
199
+ setCloudRepositoryUrl,
200
+ setEnvironment,
201
+ } from "@myop/react-native";
202
+
203
+ enableLocalDev(); // localhost:9292
204
+ setCloudRepositoryUrl("https://custom"); // Custom URL
205
+ setEnvironment("staging"); // Default environment
206
+ ```
207
+
208
+ **Local dev note:** For iOS simulator, `localhost` works. For Android emulator, use `10.0.2.2` instead. For physical devices, use your machine's IP address:
209
+
210
+ ```tsx
211
+ import { setCloudRepositoryUrl } from "@myop/react-native";
212
+
213
+ // Android emulator
214
+ setCloudRepositoryUrl("http://10.0.2.2:9292");
215
+
216
+ // Physical device (replace with your machine's IP)
217
+ setCloudRepositoryUrl("http://192.168.1.100:9292");
218
+ ```
219
+
220
+ ## Custom Loader
221
+
222
+ Pass a React Native component as the `loader` prop. The loader supports a fade-out animation via `MyopLoader`:
223
+
224
+ ```tsx
225
+ import { MyopComponent, MyopLoader } from "@myop/react-native";
226
+
227
+ <MyopComponent
228
+ componentId="..."
229
+ loader={
230
+ <MyopLoader>
231
+ <ActivityIndicator size="large" color="#007AFF" />
232
+ <Text>Loading component...</Text>
233
+ </MyopLoader>
234
+ }
235
+ fallback={<Text>Failed to load component</Text>}
236
+ fadeDuration={300}
237
+ />
238
+ ```
239
+
240
+ ## Complete Example
241
+
242
+ ```tsx
243
+ import React, { useState } from 'react';
244
+ import { View, Button, Text, StyleSheet } from 'react-native';
245
+ import { MyopComponent, preloadComponents } from '@myop/react-native';
246
+ import type { IMyopComponentProxy } from '@myop/react-native';
247
+
248
+ // Preload on module load
249
+ preloadComponents(['task-list-abc123']);
250
+
251
+ function TaskScreen() {
252
+ const [tasks, setTasks] = useState([
253
+ { id: '1', title: 'Review PR', completed: false },
254
+ { id: '2', title: 'Deploy', completed: true },
255
+ ]);
256
+
257
+ return (
258
+ <View style={styles.container}>
259
+ <Text style={styles.title}>My Tasks</Text>
260
+ <MyopComponent
261
+ componentId="task-list-abc123"
262
+ data={{ tasks }}
263
+ on={(action, payload) => {
264
+ if (action === 'task-toggled') {
265
+ setTasks(prev =>
266
+ prev.map(t =>
267
+ t.id === payload.taskId
268
+ ? { ...t, completed: payload.completed }
269
+ : t
270
+ )
271
+ );
272
+ }
273
+ if (action === 'task-deleted') {
274
+ setTasks(prev => prev.filter(t => t.id !== payload.taskId));
275
+ }
276
+ }}
277
+ onLoad={(component) => {
278
+ console.log('Component loaded:', component.id);
279
+ }}
280
+ onError={(error) => {
281
+ console.error('Failed:', error);
282
+ }}
283
+ scrollEnabled={true}
284
+ selectionEnabled={false}
285
+ style={styles.component}
286
+ />
287
+ </View>
288
+ );
289
+ }
290
+
291
+ const styles = StyleSheet.create({
292
+ container: { flex: 1 },
293
+ title: { fontSize: 24, padding: 16 },
294
+ component: { flex: 1 },
295
+ });
296
+ ```
297
+
298
+ ## Auto-Generated React Native Package
299
+
300
+ ```bash
301
+ npm install https://cloud.myop.dev/npm/{componentId}/react-native
302
+ ```
303
+
304
+ The generated package exports:
305
+ - Named component with `componentId` baked in (no `componentId` prop needed)
306
+ - Typed `data` prop from `MyopInitData`
307
+ - Typed `on[ActionName]` handlers from `MyopCtaPayloads`
308
+ - `COMPONENT_ID` constant
309
+
310
+ ## TypeScript Types
311
+
312
+ ```typescript
313
+ import type {
314
+ IMyopComponentProxy, // Component instance proxy (onLoad callback)
315
+ IComponentInstanceConfig, // Direct config object type
316
+ } from "@myop/react-native";
317
+
318
+ import { MyopLoader } from "@myop/react-native";
319
+ import type { MyopLoaderRef } from "@myop/react-native";
320
+ ```