@myop/cli 0.1.47 → 0.1.48
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/myop-cli.js +384 -379
- package/dist/skills/myop-component/SKILL.md +47 -18
- package/dist/skills/myop-component/references/component-api.md +0 -28
- package/dist/skills/myop-component/references/sizing-and-layout.md +41 -71
- package/dist/skills/myop-component/references/type-definitions.md +2 -19
- package/package.json +1 -1
|
@@ -52,25 +52,17 @@ The deployed artifact is a **single entry point** — currently an HTML file (`i
|
|
|
52
52
|
<head>
|
|
53
53
|
<meta charset="UTF-8">
|
|
54
54
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
55
|
-
<meta name="myop:size" content='{"width":"100%","height":
|
|
55
|
+
<meta name="myop:size" content='{"width":"100%","height":300}'>
|
|
56
56
|
<title>My Component</title>
|
|
57
57
|
<script type="myop/types">
|
|
58
|
+
// Only dynamic data goes here — static labels stay hardcoded in the component
|
|
58
59
|
interface MyopInitData {
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
items: Array<{ id: string; label: string; status?: 'active' | 'done' }>;
|
|
61
|
+
selectedId?: string;
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
interface MyopCtaPayloads {
|
|
64
65
|
'item-selected': { itemId: string };
|
|
65
|
-
'size-requested': {
|
|
66
|
-
width?: number | null;
|
|
67
|
-
height?: number | null;
|
|
68
|
-
minWidth?: number | null;
|
|
69
|
-
maxWidth?: number | null;
|
|
70
|
-
minHeight?: number | null;
|
|
71
|
-
maxHeight?: number | null;
|
|
72
|
-
required?: boolean;
|
|
73
|
-
};
|
|
74
66
|
}
|
|
75
67
|
|
|
76
68
|
declare function myop_init_interface(): MyopInitData;
|
|
@@ -81,10 +73,10 @@ The deployed artifact is a **single entry point** — currently an HTML file (`i
|
|
|
81
73
|
</script>
|
|
82
74
|
<style>
|
|
83
75
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
84
|
-
html, body { width: 100%;
|
|
76
|
+
html, body { width: 100%; margin: 0; padding: 0;
|
|
85
77
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
86
78
|
}
|
|
87
|
-
#app-root { width: 100%;
|
|
79
|
+
#app-root { width: 100%; padding: 16px; }
|
|
88
80
|
</style>
|
|
89
81
|
</head>
|
|
90
82
|
<body>
|
|
@@ -97,8 +89,9 @@ The deployed artifact is a **single entry point** — currently an HTML file (`i
|
|
|
97
89
|
function render(data) {
|
|
98
90
|
state = data;
|
|
99
91
|
var root = document.getElementById('app-root');
|
|
92
|
+
// "My Tasks" heading is hardcoded — it's part of the component, not dynamic data
|
|
100
93
|
root.innerHTML =
|
|
101
|
-
'<h2>
|
|
94
|
+
'<h2>My Tasks</h2>' +
|
|
102
95
|
'<ul>' + (data.items || []).map(function(item) {
|
|
103
96
|
return '<li data-id="' + item.id + '">' + item.label + '</li>';
|
|
104
97
|
}).join('') + '</ul>';
|
|
@@ -123,12 +116,12 @@ The deployed artifact is a **single entry point** — currently an HTML file (`i
|
|
|
123
116
|
})();
|
|
124
117
|
</script>
|
|
125
118
|
|
|
119
|
+
<!-- Preview: only dynamic data, not UI labels -->
|
|
126
120
|
<script id="myop_preview">
|
|
127
121
|
window.myop_init_interface({
|
|
128
|
-
title: 'My Component',
|
|
129
122
|
items: [
|
|
130
|
-
{ id: '1', label: '
|
|
131
|
-
{ id: '2', label: '
|
|
123
|
+
{ id: '1', label: 'Review PR #42' },
|
|
124
|
+
{ id: '2', label: 'Update dependencies', status: 'done' }
|
|
132
125
|
]
|
|
133
126
|
});
|
|
134
127
|
</script>
|
|
@@ -136,6 +129,42 @@ The deployed artifact is a **single entry point** — currently an HTML file (`i
|
|
|
136
129
|
</html>
|
|
137
130
|
```
|
|
138
131
|
|
|
132
|
+
## What Goes in myop_init_interface vs Component Code
|
|
133
|
+
|
|
134
|
+
Think of `myop_init_interface(data)` exactly like **React component props**. The component is a reusable UI module — its structure, labels, buttons, and layout are built-in. The host only passes dynamic data.
|
|
135
|
+
|
|
136
|
+
### Component code (hardcoded — like JSX):
|
|
137
|
+
- Button labels ("Submit", "Cancel", "Sign Up")
|
|
138
|
+
- Headings and static copy ("Welcome", "Sign up to continue")
|
|
139
|
+
- Icons, decorative elements, illustrations
|
|
140
|
+
- Form labels ("Email", "Password")
|
|
141
|
+
- Placeholder text
|
|
142
|
+
- Navigation items, tabs, section titles
|
|
143
|
+
- Any text that is part of the component's identity
|
|
144
|
+
|
|
145
|
+
### myop_init_interface data (dynamic — like React props):
|
|
146
|
+
- Lists of items (users, products, tasks, notifications)
|
|
147
|
+
- User-specific data (name, email, avatar, role)
|
|
148
|
+
- Configuration flags (theme, locale, permissions, feature flags)
|
|
149
|
+
- Counts and metrics (unread: 5, total: 42)
|
|
150
|
+
- State (selectedId, isExpanded, activeTab)
|
|
151
|
+
- URLs and dynamic assets (profile image URL, API endpoints)
|
|
152
|
+
|
|
153
|
+
### Example — Login Component:
|
|
154
|
+
|
|
155
|
+
| WRONG (everything as data) | CORRECT (only dynamic data as props) |
|
|
156
|
+
|---|---|
|
|
157
|
+
| `heading: "Welcome back"` | `user: { email: "prefilled@example.com" }` |
|
|
158
|
+
| `emailLabel: "Email"` | `error: null` |
|
|
159
|
+
| `passwordLabel: "Password"` | `redirectUrl: "/dashboard"` |
|
|
160
|
+
| `submitText: "Sign In"` | |
|
|
161
|
+
| `forgotText: "Forgot password?"` | |
|
|
162
|
+
|
|
163
|
+
The labels "Welcome back", "Email", "Password", "Sign In" are part of the component — hardcoded in the HTML/JS, just like you'd write them in React JSX.
|
|
164
|
+
|
|
165
|
+
### Rule of thumb:
|
|
166
|
+
If the host app would NOT need to change this value across different instances, it belongs in the component code, not in myop_init_interface.
|
|
167
|
+
|
|
139
168
|
## Component File Structure
|
|
140
169
|
|
|
141
170
|
### Single-file mode (simplest — good for small components)
|
|
@@ -122,25 +122,6 @@ window.myop_cta_handler(action_id, payload);
|
|
|
122
122
|
})();
|
|
123
123
|
```
|
|
124
124
|
|
|
125
|
-
### Standard Actions
|
|
126
|
-
|
|
127
|
-
These action IDs have special meaning and are handled by the Myop SDK:
|
|
128
|
-
|
|
129
|
-
| Action ID | Payload | Purpose |
|
|
130
|
-
|-----------|---------|---------|
|
|
131
|
-
| `'size-requested'` | `{ width?, height?, minWidth?, maxWidth?, minHeight?, maxHeight?, required? }` | Request the host to resize the component container |
|
|
132
|
-
|
|
133
|
-
```javascript
|
|
134
|
-
// Request more width
|
|
135
|
-
window.myop_cta_handler('size-requested', {
|
|
136
|
-
width: 400,
|
|
137
|
-
minWidth: 300,
|
|
138
|
-
maxWidth: 600,
|
|
139
|
-
required: true // true = content is clipped without this size
|
|
140
|
-
// false = preference only, host may ignore
|
|
141
|
-
});
|
|
142
|
-
```
|
|
143
|
-
|
|
144
125
|
### Custom Actions
|
|
145
126
|
|
|
146
127
|
Define any custom actions your component needs. Use kebab-case for action IDs:
|
|
@@ -226,15 +207,6 @@ The `<script id="myop_preview">` block provides mock data for development. In pr
|
|
|
226
207
|
interface MyopCtaPayloads {
|
|
227
208
|
'task-toggled': { taskId: string; completed: boolean };
|
|
228
209
|
'task-deleted': { taskId: string };
|
|
229
|
-
'size-requested': {
|
|
230
|
-
width?: number | null;
|
|
231
|
-
height?: number | null;
|
|
232
|
-
minWidth?: number | null;
|
|
233
|
-
maxWidth?: number | null;
|
|
234
|
-
minHeight?: number | null;
|
|
235
|
-
maxHeight?: number | null;
|
|
236
|
-
required?: boolean;
|
|
237
|
-
};
|
|
238
210
|
}
|
|
239
211
|
|
|
240
212
|
declare function myop_init_interface(): MyopInitData;
|
|
@@ -6,7 +6,6 @@ Myop components run inside a container managed by the host application. Proper s
|
|
|
6
6
|
- [Size Meta Tag](#size-meta-tag)
|
|
7
7
|
- [Required Base CSS](#required-base-css)
|
|
8
8
|
- [Layout Patterns](#layout-patterns)
|
|
9
|
-
- [Dynamic Size Requests](#dynamic-size-requests)
|
|
10
9
|
- [Common Mistakes](#common-mistakes)
|
|
11
10
|
|
|
12
11
|
## Size Meta Tag
|
|
@@ -14,10 +13,13 @@ Myop components run inside a container managed by the host application. Proper s
|
|
|
14
13
|
Every component should declare its preferred dimensions in a `<meta>` tag inside `<head>`:
|
|
15
14
|
|
|
16
15
|
```html
|
|
17
|
-
<meta name="myop:size" content='{"width":"100%","height":
|
|
16
|
+
<meta name="myop:size" content='{"width":"100%","height":300}'>
|
|
18
17
|
```
|
|
19
18
|
|
|
20
|
-
The host reads this BEFORE rendering the component, so the container can be sized correctly from the start (no layout shift).
|
|
19
|
+
The host reads this BEFORE rendering the component, so the container can be sized correctly from the start (no layout shift). The `height` value also determines the sizing mode:
|
|
20
|
+
|
|
21
|
+
- **Number (or absent)** → **Content mode** (default): component auto-sizes to its content, like a `<div>`
|
|
22
|
+
- **`"100%"`** → **Fill mode**: component fills its parent container
|
|
21
23
|
|
|
22
24
|
### Properties
|
|
23
25
|
|
|
@@ -26,7 +28,7 @@ All values are either a number (pixels) or the string `"100%"`:
|
|
|
26
28
|
| Property | Type | Description |
|
|
27
29
|
|----------|------|-------------|
|
|
28
30
|
| `width` | `number \| "100%"` | Preferred width in px, or `"100%"` to fill container |
|
|
29
|
-
| `height` | `number \| "100%"` | Preferred height in px, or `"100%"` to fill container |
|
|
31
|
+
| `height` | `number \| "100%"` | Preferred height in px (content mode), or `"100%"` to fill container (fill mode) |
|
|
30
32
|
| `minWidth` | `number` | Minimum acceptable width in px |
|
|
31
33
|
| `maxWidth` | `number` | Maximum useful width in px |
|
|
32
34
|
| `minHeight` | `number` | Minimum acceptable height in px |
|
|
@@ -35,13 +37,16 @@ All values are either a number (pixels) or the string `"100%"`:
|
|
|
35
37
|
### Examples
|
|
36
38
|
|
|
37
39
|
```html
|
|
38
|
-
<!--
|
|
40
|
+
<!-- Content mode (default): auto-sizes to content -->
|
|
41
|
+
<meta name="myop:size" content='{"width":"100%","height":300}'>
|
|
42
|
+
|
|
43
|
+
<!-- Fill mode: fills all available space -->
|
|
39
44
|
<meta name="myop:size" content='{"width":"100%","height":"100%"}'>
|
|
40
45
|
|
|
41
|
-
<!-- Fixed width sidebar
|
|
46
|
+
<!-- Fixed width sidebar, fills height -->
|
|
42
47
|
<meta name="myop:size" content='{"width":300,"height":"100%","minWidth":200,"maxWidth":400}'>
|
|
43
48
|
|
|
44
|
-
<!-- Fixed height banner -->
|
|
49
|
+
<!-- Fixed height banner, content mode -->
|
|
45
50
|
<meta name="myop:size" content='{"width":"100%","height":80}'>
|
|
46
51
|
|
|
47
52
|
<!-- Card with constraints -->
|
|
@@ -50,32 +55,31 @@ All values are either a number (pixels) or the string `"100%"`:
|
|
|
50
55
|
|
|
51
56
|
## Required Base CSS
|
|
52
57
|
|
|
53
|
-
|
|
58
|
+
There are two base CSS patterns depending on the sizing mode:
|
|
59
|
+
|
|
60
|
+
### Content Mode (default) — component acts like a `<div>`
|
|
61
|
+
|
|
62
|
+
The component auto-sizes to its content. Width fills the parent, height grows with content. Use for cards, forms, lists, banners, modals, and any component where the height depends on the content.
|
|
54
63
|
|
|
55
64
|
```css
|
|
56
|
-
* {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
65
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
66
|
+
html, body { width: 100%; margin: 0; padding: 0; }
|
|
67
|
+
#app-root { width: 100%; }
|
|
68
|
+
```
|
|
61
69
|
|
|
62
|
-
|
|
63
|
-
width: 100%;
|
|
64
|
-
height: 100%;
|
|
65
|
-
overflow: hidden; /* Component handles its own scrolling */
|
|
66
|
-
margin: 0;
|
|
67
|
-
padding: 0;
|
|
68
|
-
}
|
|
70
|
+
### Fill Mode (opt-in) — component fills its parent container
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
72
|
+
The component fills all available space. Use for sidebars, full-page panels, dashboards, and any component that must stretch to fit the parent.
|
|
73
|
+
|
|
74
|
+
```css
|
|
75
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
76
|
+
html, body { width: 100%; height: 100%; overflow: hidden; margin: 0; padding: 0; }
|
|
77
|
+
#app-root { width: 100%; height: 100%; }
|
|
74
78
|
```
|
|
75
79
|
|
|
76
|
-
### Why `overflow: hidden` on body?
|
|
80
|
+
### Why `overflow: hidden` on body in fill mode?
|
|
77
81
|
|
|
78
|
-
The component is rendered inside an iframe by the host. If the body scrolls, the host has no way to control or sync that scroll. Instead,
|
|
82
|
+
The component is rendered inside an iframe by the host. If the body scrolls, the host has no way to control or sync that scroll. Instead, fill-mode components should manage scrolling internally with specific scrollable regions. In content mode, overflow is omitted so the host can control it.
|
|
79
83
|
|
|
80
84
|
## Layout Patterns
|
|
81
85
|
|
|
@@ -147,47 +151,6 @@ For simple display components:
|
|
|
147
151
|
}
|
|
148
152
|
```
|
|
149
153
|
|
|
150
|
-
## Dynamic Size Requests
|
|
151
|
-
|
|
152
|
-
If the component needs to change its size after initialization (e.g., content loaded, accordion expanded), use the `size-requested` CTA:
|
|
153
|
-
|
|
154
|
-
```javascript
|
|
155
|
-
// Request specific dimensions
|
|
156
|
-
window.myop_cta_handler('size-requested', {
|
|
157
|
-
width: 400,
|
|
158
|
-
height: 600,
|
|
159
|
-
required: true // true = content will be clipped without this size
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// Request only width change
|
|
163
|
-
window.myop_cta_handler('size-requested', {
|
|
164
|
-
width: 500,
|
|
165
|
-
minWidth: 350,
|
|
166
|
-
maxWidth: 700
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
// Request based on content height
|
|
170
|
-
var contentHeight = document.getElementById('content').scrollHeight;
|
|
171
|
-
window.myop_cta_handler('size-requested', {
|
|
172
|
-
height: contentHeight + 40, // Add padding
|
|
173
|
-
required: false // Preference, not critical
|
|
174
|
-
});
|
|
175
|
-
```
|
|
176
|
-
|
|
177
|
-
### Size Request Payload
|
|
178
|
-
|
|
179
|
-
| Property | Type | Description |
|
|
180
|
-
|----------|------|-------------|
|
|
181
|
-
| `width` | `number \| null` | Desired width in px (`null` = no preference) |
|
|
182
|
-
| `height` | `number \| null` | Desired height in px (`null` = no preference) |
|
|
183
|
-
| `minWidth` | `number \| null` | Minimum acceptable width |
|
|
184
|
-
| `maxWidth` | `number \| null` | Maximum acceptable width |
|
|
185
|
-
| `minHeight` | `number \| null` | Minimum acceptable height |
|
|
186
|
-
| `maxHeight` | `number \| null` | Maximum acceptable height |
|
|
187
|
-
| `required` | `boolean` | `true` = content is clipped/broken without this size; `false` = preference only |
|
|
188
|
-
|
|
189
|
-
**Important:** The host application may choose to ignore size requests. Always design the component to be functional at any reasonable size, and use `required: true` sparingly.
|
|
190
|
-
|
|
191
154
|
## Common Mistakes
|
|
192
155
|
|
|
193
156
|
### Missing `min-height: 0` on flex children
|
|
@@ -207,15 +170,15 @@ window.myop_cta_handler('size-requested', {
|
|
|
207
170
|
}
|
|
208
171
|
```
|
|
209
172
|
|
|
210
|
-
### Body scrolling instead of internal scrolling
|
|
173
|
+
### Body scrolling instead of internal scrolling (fill mode)
|
|
211
174
|
|
|
212
175
|
```css
|
|
213
|
-
/* WRONG: Body scrolls, host can't control it */
|
|
176
|
+
/* WRONG: Body scrolls in fill mode, host can't control it */
|
|
214
177
|
html, body {
|
|
215
178
|
overflow: auto;
|
|
216
179
|
}
|
|
217
180
|
|
|
218
|
-
/* CORRECT: Body never scrolls, content region scrolls */
|
|
181
|
+
/* CORRECT (fill mode): Body never scrolls, content region scrolls */
|
|
219
182
|
html, body {
|
|
220
183
|
overflow: hidden;
|
|
221
184
|
}
|
|
@@ -224,6 +187,8 @@ html, body {
|
|
|
224
187
|
}
|
|
225
188
|
```
|
|
226
189
|
|
|
190
|
+
Note: In content mode, body overflow is not set — the host controls overflow.
|
|
191
|
+
|
|
227
192
|
### Forgetting `box-sizing: border-box`
|
|
228
193
|
|
|
229
194
|
```css
|
|
@@ -252,11 +217,16 @@ html, body {
|
|
|
252
217
|
height: 400px;
|
|
253
218
|
}
|
|
254
219
|
|
|
255
|
-
/* CORRECT: Fills whatever container the host provides */
|
|
220
|
+
/* CORRECT (fill mode): Fills whatever container the host provides */
|
|
256
221
|
#app-root {
|
|
257
222
|
width: 100%;
|
|
258
223
|
height: 100%;
|
|
259
224
|
}
|
|
225
|
+
|
|
226
|
+
/* CORRECT (content mode): Width fills, height is content-driven */
|
|
227
|
+
#app-root {
|
|
228
|
+
width: 100%;
|
|
229
|
+
}
|
|
260
230
|
```
|
|
261
231
|
|
|
262
232
|
### Size meta tag with invalid JSON
|
|
@@ -135,16 +135,6 @@ interface MyopCtaPayloads {
|
|
|
135
135
|
'page-changed': { page: number };
|
|
136
136
|
'tab-switched': { tabId: string };
|
|
137
137
|
|
|
138
|
-
// Standard Myop actions (always include if component needs resizing)
|
|
139
|
-
'size-requested': {
|
|
140
|
-
width?: number | null;
|
|
141
|
-
height?: number | null;
|
|
142
|
-
minWidth?: number | null;
|
|
143
|
-
maxWidth?: number | null;
|
|
144
|
-
minHeight?: number | null;
|
|
145
|
-
maxHeight?: number | null;
|
|
146
|
-
required?: boolean;
|
|
147
|
-
};
|
|
148
138
|
}
|
|
149
139
|
```
|
|
150
140
|
|
|
@@ -152,9 +142,8 @@ interface MyopCtaPayloads {
|
|
|
152
142
|
|
|
153
143
|
1. **Use kebab-case** for action names: `'item-selected'` not `'itemSelected'`
|
|
154
144
|
2. **Use `void`** for actions with no payload (e.g., `'refresh-requested': void`)
|
|
155
|
-
3. **
|
|
156
|
-
4. **
|
|
157
|
-
5. **Every action called in code must be defined here** - keep types in sync with implementation
|
|
145
|
+
3. **Payload must be JSON-serializable** - no functions, DOM elements, or class instances
|
|
146
|
+
4. **Every action called in code must be defined here** - keep types in sync with implementation
|
|
158
147
|
|
|
159
148
|
## Standard Declarations
|
|
160
149
|
|
|
@@ -205,12 +194,6 @@ interface MyopInitData {
|
|
|
205
194
|
|
|
206
195
|
interface MyopCtaPayloads {
|
|
207
196
|
'point-clicked': { datasetIndex: number; pointIndex: number; value: number };
|
|
208
|
-
'size-requested': {
|
|
209
|
-
width?: number | null; height?: number | null;
|
|
210
|
-
minWidth?: number | null; maxWidth?: number | null;
|
|
211
|
-
minHeight?: number | null; maxHeight?: number | null;
|
|
212
|
-
required?: boolean;
|
|
213
|
-
};
|
|
214
197
|
}
|
|
215
198
|
```
|
|
216
199
|
|