@embeddables/cli 0.2.0 → 0.3.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/.cursor/rules/embeddables-cli.md +679 -0
- package/dist/cli.js +24 -7
- package/dist/commands/dev.d.ts +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +44 -5
- package/dist/commands/init.d.ts +0 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +143 -86
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +20 -0
- package/dist/commands/save.d.ts +8 -0
- package/dist/commands/save.d.ts.map +1 -0
- package/dist/commands/save.js +269 -0
- package/dist/compiler/index.d.ts.map +1 -1
- package/dist/compiler/index.js +4 -0
- package/dist/compiler/reverse.d.ts.map +1 -1
- package/dist/compiler/reverse.js +4 -10
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -0
- package/dist/prompts/projects.d.ts +1 -0
- package/dist/prompts/projects.d.ts.map +1 -1
- package/dist/prompts/projects.js +1 -0
- package/dist/proxy/server.d.ts.map +1 -1
- package/dist/proxy/server.js +10 -4
- package/package.json +2 -1
|
@@ -0,0 +1,679 @@
|
|
|
1
|
+
---
|
|
2
|
+
globs:
|
|
3
|
+
alwaysApply: true
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cursor Rules for Embeddables CLI
|
|
7
|
+
|
|
8
|
+
This document provides comprehensive context for Cursor AI when working with Embeddables in the CLI codebase. The CLI transforms Embeddable JSON into a local file structure for development, then compiles it back to JSON for saving.
|
|
9
|
+
|
|
10
|
+
**Note**: All TypeScript types are defined in `src/types-builder.ts`. Refer to that file for complete type definitions. This document focuses on contextual information about how the system works.
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
The CLI transforms an Embeddable JSON into:
|
|
15
|
+
|
|
16
|
+
- **React files**: Pages and global components
|
|
17
|
+
- **CSS files**: Styles organized by selectors
|
|
18
|
+
- **JS files**: Computed fields and actions
|
|
19
|
+
- **config.json**: Reduced JSON containing structure/metadata without file content
|
|
20
|
+
|
|
21
|
+
## Embeddable JSON Structure (Flow)
|
|
22
|
+
|
|
23
|
+
The root Embeddable object (called `Flow`) is defined in `src/types-builder.ts`. Key properties:
|
|
24
|
+
|
|
25
|
+
- `pages: FlowPage[]` → React page files
|
|
26
|
+
- `styles?: FlowStyles` → CSS files
|
|
27
|
+
- `components?: Component[]` → React global component files, one file per 'location'
|
|
28
|
+
- `dataOutputs?: Action[]` → JS action files
|
|
29
|
+
- `computedFields?: ComputedField[]` → JS computed field files
|
|
30
|
+
- All other properties → config.json
|
|
31
|
+
|
|
32
|
+
## Pages Structure
|
|
33
|
+
|
|
34
|
+
Pages are stored in `Flow.pages` and transform to React page files. The `FlowPage` type is defined in `src/types-builder.ts`.
|
|
35
|
+
|
|
36
|
+
**Key Properties**:
|
|
37
|
+
|
|
38
|
+
- `key: string` - Used for file naming (e.g., "welcome_page" → `pages/welcome_page.page.tsx`)
|
|
39
|
+
- `components: Component[]` - Components on this page → React JSX
|
|
40
|
+
- `tags?: string[]` - Used for CSS styling
|
|
41
|
+
- `conditions?: Condition[]` - Visibility logic
|
|
42
|
+
- `is_exit_page` - Whether a user viewing this page should be disallowed from proceeding any further (e.g. a disqualification page)
|
|
43
|
+
- All other properties stored in config.json (along with key, tags, conditions).
|
|
44
|
+
|
|
45
|
+
**File Mapping**: `pages/{pageKey}.page.tsx` (e.g., `pages/welcome_page.page.tsx`)
|
|
46
|
+
|
|
47
|
+
**React Structure**: Each page file exports a React component that renders the page's components as JSX.
|
|
48
|
+
|
|
49
|
+
## Global Components Structure
|
|
50
|
+
|
|
51
|
+
Global components are stored in `Flow.components` (not in pages) and have a `_location` property indicating where they appear. The `GlobalComponentLocation` type is defined in `src/types-builder.ts`.
|
|
52
|
+
|
|
53
|
+
**Global Component Locations**:
|
|
54
|
+
|
|
55
|
+
<!-- todo - describe these better in terms of HTML elements -->
|
|
56
|
+
|
|
57
|
+
- `"before_page"` - Before the page container
|
|
58
|
+
- `"page_top"` - At the top of the page
|
|
59
|
+
- `"before_components"` - Before page components
|
|
60
|
+
- `"after_components"` - After page components
|
|
61
|
+
- `"page_bottom"` - At the bottom of the page
|
|
62
|
+
- `"after_page"` - After the page container
|
|
63
|
+
|
|
64
|
+
**File Mapping**: `global-components/{locationKey}.location.tsx` (e.g., `global-components/after_page.location.tsx`)
|
|
65
|
+
|
|
66
|
+
**Important**: Global components have `_location` property that determines where they render relative to pages. This is how they are grouped into files.
|
|
67
|
+
|
|
68
|
+
## Component Types and Structure
|
|
69
|
+
|
|
70
|
+
All component types are derived from `src/types-builder.ts`. The relevant typings are:
|
|
71
|
+
|
|
72
|
+
- `Component` - Union type of all component interfaces
|
|
73
|
+
- `ComponentType` - Union type of all component type strings
|
|
74
|
+
- `Base` - Base interface that all components extend
|
|
75
|
+
- Specific component interfaces, which correspons to the 'type' property in components: `Container`, `PlainText`, `RichTextMarkdown`, `CustomButton`, `InputBox`, `OptionSelector`, `MediaImage`, `StripeCheckout2`, `PaypalCheckout`, `FileUpload`, `ProgressBar`, `BookMeeting`, `Chart`, `Lottie`, `Rive`, `MediaEmbed`, etc.
|
|
76
|
+
|
|
77
|
+
**Key Base Properties** (all components have these):
|
|
78
|
+
|
|
79
|
+
- `id: string` - Unique identifier - this must be unique across the entire Embeddable JSON. Always snake_case.
|
|
80
|
+
- `key: string` - Unique identifier, used in React as key prop - unique in general but duplicate keys can be used in certain cases (e.g. two `email` fields, each hidden by conditions). Always snake_case.
|
|
81
|
+
- `type: ComponentType` - Component type
|
|
82
|
+
- `tags?: string[]` - Used for CSS styling
|
|
83
|
+
- `parent_id?: string` - For nested components
|
|
84
|
+
- `_location?: GlobalComponentLocation` - Only for global components
|
|
85
|
+
- Many other optional properties - see `src/types-builder.ts` for complete list
|
|
86
|
+
|
|
87
|
+
**Component-Specific Properties**: Each component type has additional properties. Refer to `src/types-builder.ts` for:
|
|
88
|
+
|
|
89
|
+
- `CustomButton`
|
|
90
|
+
- `action`
|
|
91
|
+
- `actionUrl`
|
|
92
|
+
- `icon`
|
|
93
|
+
- `outputs_onclick`
|
|
94
|
+
- etc.
|
|
95
|
+
- `InputBox`
|
|
96
|
+
- `input_type`
|
|
97
|
+
- `validation_formula`
|
|
98
|
+
- `outputs_onchange`
|
|
99
|
+
- etc.
|
|
100
|
+
- `OptionSelector`
|
|
101
|
+
- `buttons`
|
|
102
|
+
- `multiple` - whether to allow multi-select
|
|
103
|
+
- `dropdown`
|
|
104
|
+
- `checkbox` - whether to add a (square/round) visual checkbox in each button (the checkbox will be added for you, no need to add it manually)
|
|
105
|
+
- `checkbox_location` whether to put the checkboxes at the start or end of the button contents
|
|
106
|
+
- etc.
|
|
107
|
+
- `MediaImage`
|
|
108
|
+
- `caption`
|
|
109
|
+
- `alt_text`
|
|
110
|
+
- `Container`
|
|
111
|
+
- `container_type`
|
|
112
|
+
- And all other component types
|
|
113
|
+
|
|
114
|
+
**Component hierarchy and nesting**: Components in the JSON are always in a flat array - the containment of other components is handled by the `parent_id` property, which corresponds to the `id` of another component.
|
|
115
|
+
|
|
116
|
+
### React Component Rendering
|
|
117
|
+
|
|
118
|
+
When transforming to React:
|
|
119
|
+
|
|
120
|
+
- Each component becomes a JSX element with props matching the component's properties
|
|
121
|
+
- Component `id` prop must match the component's `id` property
|
|
122
|
+
- Component `key` prop must match the component's `key` property
|
|
123
|
+
- Component `type` determines the React component to render
|
|
124
|
+
- Nested components (from `parent_id`) are rendered as children
|
|
125
|
+
- Tags are applied as CSS classes (e.g. `.ComponentTag-{tagName}` - see below)
|
|
126
|
+
|
|
127
|
+
**Example React transformation**:
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
// JSON:
|
|
131
|
+
{
|
|
132
|
+
"id": "comp_123",
|
|
133
|
+
"key": "hero_title",
|
|
134
|
+
"type": "PlainText",
|
|
135
|
+
"text": "Welcome to our site",
|
|
136
|
+
"tags": ["hero_text"]
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// React:
|
|
140
|
+
<PlainText id="comp_123" key="hero_title" text="Welcome to our site" tags={["hero_text"]} />
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**React file export convention**: All React page and global component files must use **default exports**:
|
|
144
|
+
|
|
145
|
+
```tsx
|
|
146
|
+
// Correct - use default export
|
|
147
|
+
export default function PageName() {
|
|
148
|
+
return (...)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Incorrect - do NOT use named export
|
|
152
|
+
export function PageName() {
|
|
153
|
+
return (...)
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**OptionSelector buttons pattern**: When writing TSX files with `OptionSelector` components, define the buttons as constants before the JSX, then reference them in the `buttons` prop:
|
|
158
|
+
|
|
159
|
+
```tsx
|
|
160
|
+
'use client'
|
|
161
|
+
|
|
162
|
+
import { OptionSelector } from '@embeddables/cli/components'
|
|
163
|
+
import type { OptionSelectorButton } from '@embeddables/cli/types'
|
|
164
|
+
|
|
165
|
+
const planButtons: OptionSelectorButton[] = [
|
|
166
|
+
{ id: 'button_plan_basic', key: 'basic', text: 'Basic Plan', description: '$9/month' },
|
|
167
|
+
{ id: 'button_plan_pro', key: 'pro', text: 'Pro Plan', description: '$19/month' },
|
|
168
|
+
{
|
|
169
|
+
id: 'button_plan_enterprise',
|
|
170
|
+
key: 'enterprise',
|
|
171
|
+
text: 'Enterprise',
|
|
172
|
+
description: 'Contact us',
|
|
173
|
+
},
|
|
174
|
+
]
|
|
175
|
+
|
|
176
|
+
export default function PlanPage() {
|
|
177
|
+
return (
|
|
178
|
+
<OptionSelector
|
|
179
|
+
id="comp_456"
|
|
180
|
+
key="plan_selector"
|
|
181
|
+
buttons={planButtons}
|
|
182
|
+
tags={['plan_options']}
|
|
183
|
+
/>
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
This pattern improves readability and makes the buttons array easier to maintain compared to defining it inline within the JSX.
|
|
189
|
+
|
|
190
|
+
## Styles Structure (CSS)
|
|
191
|
+
|
|
192
|
+
Styles are stored in `Flow.styles` as a map of CSS selectors to style objects. The `FlowStyles` type is defined in `src/types-builder.ts`.
|
|
193
|
+
|
|
194
|
+
**File Mapping**: A single `styles.css` file
|
|
195
|
+
|
|
196
|
+
### CSS Selector System
|
|
197
|
+
|
|
198
|
+
CSS selectors follow a specific structure. The order is:
|
|
199
|
+
|
|
200
|
+
<!-- todo - check this -->
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
[mainFlowElement][breakpoint][scope][element][tag][state][subElement][option][childElement]
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
#### 1. Main Flow Element
|
|
207
|
+
|
|
208
|
+
- `.Flow-EntireFlow` - Wraps entire embeddable (used with breakpoints or global styles)
|
|
209
|
+
|
|
210
|
+
#### 2. Breakpoint
|
|
211
|
+
|
|
212
|
+
Note that these are desktop-first, not mobile-first (i.e. that the default styles are always applied, whereas mobile styles are only applied on small screen sizes).
|
|
213
|
+
|
|
214
|
+
- Desktop (default): omitted
|
|
215
|
+
- Tablet: `[max-width~='720px'] ` (note trailing space)
|
|
216
|
+
- Mobile: `[max-width~='520px'] ` (note trailing space)
|
|
217
|
+
|
|
218
|
+
Note: If you need to use any breakpoints other than 720px and 520px, you must add them in a top-level `breakpoints` (typing is Breakpoint[]) property in config.json.
|
|
219
|
+
|
|
220
|
+
#### 3. Scope
|
|
221
|
+
|
|
222
|
+
- Entire embeddable (default): omitted
|
|
223
|
+
- This page: `.PageKey-{pageKey}`
|
|
224
|
+
- Pages with tag: `.PageTag-{pageTag}`
|
|
225
|
+
|
|
226
|
+
**Important**: `PageKey-{pageKey}` and `PageTag-{pageTag}` classes are applied to **every component** on that page to identify which page the component belongs to. To actually target the **page element itself** (not just components on that page), you must combine the scope class with `.Flow-Page` (e.g., `.Flow-Page.PageTag-{pageTag}`).
|
|
227
|
+
|
|
228
|
+
#### 4. Element (Base Structure)
|
|
229
|
+
|
|
230
|
+
- Component: `.Flow-Component` or `.Flow-Component.ComponentType-{Type}`
|
|
231
|
+
- Page: `.Flow-Page` (must be combined with scope class like `.PageTag-{pageTag}` or `.PageKey-{pageKey}` to target the page element)
|
|
232
|
+
- Element: `.Flow-Element` or `.Flow-Element.ElementType-{Type}`
|
|
233
|
+
- Special: `.ElementType-PageContents`, `.InfoBox`, `.close`
|
|
234
|
+
|
|
235
|
+
#### 5. Tag (REQUIRED if targeting component/page)
|
|
236
|
+
|
|
237
|
+
- Component: `.ComponentTag-{tagName}`
|
|
238
|
+
- Page: `.PageTag-{tagName}`
|
|
239
|
+
|
|
240
|
+
#### 6. State
|
|
241
|
+
|
|
242
|
+
- `always` → omitted (default)
|
|
243
|
+
- `hover` → `:hover`
|
|
244
|
+
- `selected` → `.selected` (NOT `:selected`)
|
|
245
|
+
- `disabled` → `[disabled=disabled]` (NOT `:disabled`)
|
|
246
|
+
- `active` → `:active`
|
|
247
|
+
- `focus` → `:focus`
|
|
248
|
+
- `hover_selected` → `.selected:hover`
|
|
249
|
+
- `toggled` → `.isOn`
|
|
250
|
+
- `hover_toggled` → `.isOn:hover`
|
|
251
|
+
|
|
252
|
+
#### 7. Sub-Element (Descendant)
|
|
253
|
+
|
|
254
|
+
- Format: ` .ElementType-{Type}` (leading space required!)
|
|
255
|
+
- See the 'Component Sub-Element Types Reference' section below for details
|
|
256
|
+
|
|
257
|
+
#### 8. Option (For Option Selectors)
|
|
258
|
+
|
|
259
|
+
- Option selector: `.ElementKey-{key}`
|
|
260
|
+
- Advanced dropdown: `[value='{key}']`
|
|
261
|
+
|
|
262
|
+
#### 9. Child Element
|
|
263
|
+
|
|
264
|
+
- Format: `>img`, `>span`, `>:first-child`
|
|
265
|
+
|
|
266
|
+
### Selector Examples
|
|
267
|
+
|
|
268
|
+
<!-- todo - check these -->
|
|
269
|
+
|
|
270
|
+
```
|
|
271
|
+
// Simple component with tag
|
|
272
|
+
.Flow-Component.ComponentTag-primary_button
|
|
273
|
+
|
|
274
|
+
// Component with hover state
|
|
275
|
+
.Flow-Component.ComponentTag-primary_button:hover
|
|
276
|
+
|
|
277
|
+
// Button text on hover
|
|
278
|
+
.Flow-Component.ComponentType-CustomButton.ComponentTag-primary_button:hover.ElementType-ButtonText
|
|
279
|
+
|
|
280
|
+
// Mobile breakpoint
|
|
281
|
+
.Flow-EntireFlow[max-width~='1024px'] .Flow-Component.ComponentTag-landing_container
|
|
282
|
+
|
|
283
|
+
// Page-scoped component (PageKey is on the component, not the page)
|
|
284
|
+
.PageKey-checkout_page_1.Flow-Component.ComponentTag-checkout_button
|
|
285
|
+
|
|
286
|
+
// Page Tag-scoped component (PageTag is related to the page but the class is present on the component)
|
|
287
|
+
.PageTag-standard_page.Flow-Component.ComponentTag-header_container
|
|
288
|
+
|
|
289
|
+
// Targeting the page element itself (requires both PageTag/PageKey AND Flow-Page)
|
|
290
|
+
.Flow-Page.PageTag-intro
|
|
291
|
+
.Flow-Page.PageKey-checkout_page_1
|
|
292
|
+
|
|
293
|
+
// Option button inside OptionSelector
|
|
294
|
+
.Flow-Element.ElementType-OptionButtonCard.ComponentTag-checkbox_options
|
|
295
|
+
|
|
296
|
+
// Selected option button inside dropdown OptionSelector
|
|
297
|
+
.Flow-Element.ElementType-DropdownListOption.ComponentTag-standard_dropdown.selected
|
|
298
|
+
|
|
299
|
+
// Disabled button
|
|
300
|
+
.Flow-Component.ComponentTag-standard_button[disabled=disabled]
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Images and Icons
|
|
304
|
+
|
|
305
|
+
Whenever you need to insert an image or icon (e.g. a logo, an icon, a larger image) you have the following options:
|
|
306
|
+
|
|
307
|
+
1. `emojiIcon` with an emoji when there is an appropriate emoji that you can use. This can be used in `imageUrl` in CustomButton, or `imageUrl` inside OptionSelector buttons
|
|
308
|
+
2. Image URL. This can be used in `src` in MediaImage, `imageUrl` in CustomButton, or `imageUrl` inside OptionSelector buttons.
|
|
309
|
+
|
|
310
|
+
For placeholder images, the recommended service is placehold.co:
|
|
311
|
+
|
|
312
|
+
- https://placehold.co/600x400?text=My+Text+To+Display if there is text in the image (can't be emojis)
|
|
313
|
+
- otherwise https://placehold.co/600x400
|
|
314
|
+
|
|
315
|
+
(with the correct size specified in the URL in either case).
|
|
316
|
+
|
|
317
|
+
Please be consistent with your choice out of those options within a particular component or section.
|
|
318
|
+
|
|
319
|
+
## Component Sub-Element Types Reference
|
|
320
|
+
|
|
321
|
+
### OptionSelector
|
|
322
|
+
|
|
323
|
+
NOTE: button icons and checkboxes are before text in the button, unless you set the flex-direction to be row-reverse or column-reverse.
|
|
324
|
+
NOTE: OptionSelectors are either dropdowns (`dropdown` is `true`, can use other dropdown-related properties) or they are not. There is no half and half.
|
|
325
|
+
|
|
326
|
+
| ElementType | Element | Condition | Notes |
|
|
327
|
+
| ---------------------------------- | ------------------ | ---------------------------------------------------- | ----------------------------------------------------------------- |
|
|
328
|
+
| `Label` | `<label>` | `label` set | |
|
|
329
|
+
| `OptionButtonList` | `<div>` | `dropdown: false` | |
|
|
330
|
+
| `OptionButtonCard` | `<div>` | `dropdown: false` | |
|
|
331
|
+
| `OptionButtonCardText` | `<div>` | `dropdown: false` + button has `text` | |
|
|
332
|
+
| `OptionButtonCardDescription` | `<div>` | `dropdown: false` + button has `description` | |
|
|
333
|
+
| `OptionButtonCardIcon` | `<span>`/`<div>` | `dropdown: false` + button has `emojiIcon` or `icon` | |
|
|
334
|
+
| `OptionButtonCardIconImage` | `<span>` | `dropdown: false` + button has `imageUrl` | |
|
|
335
|
+
| `OptionButtonCardIconImageElement` | `<img>` | `dropdown: false` + button has `imageUrl` | |
|
|
336
|
+
| `OptionButtonCardCheckbox` | `<div>` | `dropdown: false` + `checkbox: true` | The checkbox is added automatically as an svg inside this element |
|
|
337
|
+
| `OptionDropdown` | `<select>`/`<div>` | `dropdown: true` | |
|
|
338
|
+
| `DropdownMain` | `<div>` | `dropdown: true` + advanced\* | |
|
|
339
|
+
| `DropdownMainContent` | `<span>` | `dropdown: true` + advanced\* | |
|
|
340
|
+
| `DropdownMainContentInput` | `<input>` | `dropdown: true` + advanced\* + `allow_typing: true` | |
|
|
341
|
+
| `DropdownList` | `<datalist>` | `dropdown: true` + advanced\* | |
|
|
342
|
+
| `DropdownListOption` | `<div>` | `dropdown: true` + advanced\* | |
|
|
343
|
+
| `DropdownListOptionText` | `<div>` | `dropdown: true` + advanced\* | |
|
|
344
|
+
|
|
345
|
+
\*Advanced = `is_advanced_dropdown` (only if `dropdown` is `true`), `multiple`, or `allow_typing` (searchable, only if `dropdown` is `true`)
|
|
346
|
+
|
|
347
|
+
### CustomButton
|
|
348
|
+
|
|
349
|
+
Note: the component itself IS the button. There is no `button` element inside it.
|
|
350
|
+
Note: button icons and checkboxes are before text in the button, unless you set the flex-direction to be row-reverse or column-reverse.
|
|
351
|
+
|
|
352
|
+
| ElementType | Element | Condition | Notes |
|
|
353
|
+
| ------------------- | ---------------- | ------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
354
|
+
| `ButtonTextWrapper` | `<div>` | Always | |
|
|
355
|
+
| `ButtonText` | `<div>` | `text` set | |
|
|
356
|
+
| `ButtonDescription` | `<div>` | `description` set | |
|
|
357
|
+
| `ButtonIcon` | `<svg>`/`<span>` | `icon` or `emojiIcon` set | If `icon`, must be font-awesome icon name; if `emojiIcon` must be an emoji in text format like 😀 |
|
|
358
|
+
| `ButtonIconImage` | `<span>` | `imageUrl` set | |
|
|
359
|
+
|
|
360
|
+
### InputBox
|
|
361
|
+
|
|
362
|
+
| ElementType | Element | Condition |
|
|
363
|
+
| ----------------- | ------------ | -------------------------------------- |
|
|
364
|
+
| `Label` | `<label>` | `label` set |
|
|
365
|
+
| `InputElement` | `<input>` | NOT (`multiline` + `input_type: text`) |
|
|
366
|
+
| `TextareaElement` | `<textarea>` | `multiline: true` + `input_type: text` |
|
|
367
|
+
|
|
368
|
+
### ChatMessages
|
|
369
|
+
|
|
370
|
+
| ElementType | Element | Condition |
|
|
371
|
+
| ---------------------------------- | ------------ | ---------------------------- |
|
|
372
|
+
| `ChatMessagesContainer` | `<div>` | Always |
|
|
373
|
+
| `ChatMessageInputContainer` | `<div>` | `use_external_inputs: false` |
|
|
374
|
+
| `ChatMessageInput` | `<textarea>` | `use_external_inputs: false` |
|
|
375
|
+
| `ChatSubmitButton` | `<button>` | `use_external_inputs: false` |
|
|
376
|
+
| `ChatMessageLoading` | `<img>` | `use_external_inputs: false` |
|
|
377
|
+
| `ChatQuickInitialOptionsContainer` | `<div>` | `quick_initial_options` set |
|
|
378
|
+
| `ChatQuickInitialOptions` | `<button>` | `quick_initial_options` set |
|
|
379
|
+
|
|
380
|
+
### MediaEmbed
|
|
381
|
+
|
|
382
|
+
| ElementType | Element | Condition |
|
|
383
|
+
| ------------------ | ---------- | ----------------------------------------------------- |
|
|
384
|
+
| `Video` | `<video>` | `src` ends with `.mp4`/`.mov` |
|
|
385
|
+
| `PlaceholderImage` | `<img>` | Video + `placeholder_image_url` + `centralPlayButton` |
|
|
386
|
+
| `Embed` | `<div>` | `embed_code` set (not video) |
|
|
387
|
+
| `IFrameContainer` | `<div>` | `src` set (not video, no embed_code) |
|
|
388
|
+
| `IFrame` | `<iframe>` | `src` set (not video, no embed_code) |
|
|
389
|
+
| `PlayButton` | `<button>` | `centralPlayButton: true` + controls shown |
|
|
390
|
+
|
|
391
|
+
### MediaImage
|
|
392
|
+
|
|
393
|
+
| ElementType | Element | Condition |
|
|
394
|
+
| ----------- | ------- | ------------- |
|
|
395
|
+
| `Image` | `<img>` | Always |
|
|
396
|
+
| `Caption` | `<div>` | `caption` set |
|
|
397
|
+
|
|
398
|
+
### ProgressBar
|
|
399
|
+
|
|
400
|
+
EXCEPTION: ProgressBarBackground does not currently exist so use the overall component for background color etc.
|
|
401
|
+
EXCEPTION: for 'ProgressBarFilled', instead of adding the 'ElementType-ProgressBarFilled' class, just add the 'bar' class.
|
|
402
|
+
NOTE: there is also a reset icon button with the class `reset` - hide this if you don't want to show it.
|
|
403
|
+
|
|
404
|
+
| ElementType | Element | Condition |
|
|
405
|
+
| ----------------------- | ------------------ | ----------------------------------- |
|
|
406
|
+
| `ProgressBarBackground` | `<div>`/`<circle>` | `progress_type: simple` or `circle` |
|
|
407
|
+
| `ProgressBarFilled` | `<div>`/`<circle>` | `progress_type: simple` or `circle` |
|
|
408
|
+
|
|
409
|
+
### FileUpload
|
|
410
|
+
|
|
411
|
+
| ElementType | Element | Condition |
|
|
412
|
+
| ----------- | ------- | --------- |
|
|
413
|
+
| `Uploader` | `<div>` | Always |
|
|
414
|
+
|
|
415
|
+
### StripeCheckout2
|
|
416
|
+
|
|
417
|
+
| ElementType | Element | Condition |
|
|
418
|
+
| ----------------------- | ---------- | ---------------------------------------- |
|
|
419
|
+
| `StripeCheckout2Button` | `<button>` | `elements_to_display` includes `payment` |
|
|
420
|
+
|
|
421
|
+
### Components with No ElementTypes
|
|
422
|
+
|
|
423
|
+
`BookMeeting`, `Chart`, `ChildFlow`, `CustomHTML`, `Lottie`, `PaypalCheckout`, `PlainText`, `RichTextMarkdown`, `Rive`
|
|
424
|
+
|
|
425
|
+
## Computed Fields Structure
|
|
426
|
+
|
|
427
|
+
Computed fields are stored in `Flow.computedFields` and transform to JS files. The `ComputedField` type is defined in `src/types-builder.ts`.
|
|
428
|
+
|
|
429
|
+
**Key Properties**:
|
|
430
|
+
|
|
431
|
+
- `key: string` - Used for file naming (e.g., "total_price" → `computed-fields/total_price.js`)
|
|
432
|
+
- `code?: string` - JavaScript function code → JS file content
|
|
433
|
+
- `inputs?: string[]` - Keys from User Data that trigger recalculation
|
|
434
|
+
- `input_triggers?: string[]` - Additional trigger keys
|
|
435
|
+
- All other properties stored in config.json
|
|
436
|
+
|
|
437
|
+
**File Mapping**: `computed-fields/{key}.js` (e.g., `computed-fields/total_price.js`)
|
|
438
|
+
|
|
439
|
+
**Code Structure**: The `code` property contains JavaScript that must include a function called `result` that returns the computed value:
|
|
440
|
+
|
|
441
|
+
```javascript
|
|
442
|
+
function result() {
|
|
443
|
+
// Access user data via global userData object
|
|
444
|
+
const price = userData.price || 0
|
|
445
|
+
const quantity = userData.quantity || 0
|
|
446
|
+
return price * quantity
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
**Important**:
|
|
451
|
+
|
|
452
|
+
- Computed fields can reference other computed fields via their keys in `inputs`
|
|
453
|
+
- The function has access to a global `userData` object containing all user data
|
|
454
|
+
- If `async` is true, the function can return a Promise
|
|
455
|
+
- `input_triggers` specify additional keys that trigger recalculation
|
|
456
|
+
|
|
457
|
+
## Actions Structure
|
|
458
|
+
|
|
459
|
+
Actions are stored in `Flow.dataOutputs` and transform to JS files. The `Action` type and extended action types (`AirtableAction`, `HubspotAction`, `GoogleSheetsAction`, `WebhookAction`, `SendgridAction`) are defined in `src/types-builder.ts`.
|
|
460
|
+
|
|
461
|
+
**Key Properties**:
|
|
462
|
+
|
|
463
|
+
- `name: string` - Used for file naming (e.g., "submit_form" → `actions/submit_form.js`)
|
|
464
|
+
- `output: "custom" | "airtable" | "hubspot" | "google-sheets" | "sendgrid" | "diahook"`
|
|
465
|
+
- `code?: string` - JavaScript function code → JS file content (for custom actions)
|
|
466
|
+
- Extended action types have additional properties (mappings, base, table, etc.) - see `src/types-builder.ts`
|
|
467
|
+
|
|
468
|
+
**File Mapping**: `actions/{name}.js` (e.g., `actions/submit_form.js`)
|
|
469
|
+
|
|
470
|
+
**Code Structure**: For `output: "custom"`, the `code` property contains JavaScript that must include a function called `output`:
|
|
471
|
+
|
|
472
|
+
```javascript
|
|
473
|
+
function output() {
|
|
474
|
+
// Access user data via global userData object
|
|
475
|
+
const email = userData.email
|
|
476
|
+
const name = userData.name
|
|
477
|
+
|
|
478
|
+
// Perform action (API call, data transformation, etc.)
|
|
479
|
+
return {
|
|
480
|
+
success: true,
|
|
481
|
+
data: { email, name },
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
**Important**:
|
|
487
|
+
|
|
488
|
+
- Actions are triggered by various events (button clicks, page completion, etc.)
|
|
489
|
+
- The function has access to a global `userData` object
|
|
490
|
+
- For non-custom actions (airtable, hubspot, etc.), the action configuration is stored in config.json, not in the JS file
|
|
491
|
+
|
|
492
|
+
## Conditions Structure
|
|
493
|
+
|
|
494
|
+
Conditions control visibility of components and pages based on User Data. The `Condition`, `ConditionOperator`, `ConditionValue`, and `ConditionalTag` types are defined in `src/types-builder.ts`.
|
|
495
|
+
|
|
496
|
+
**Valid Operators** (`ConditionOperator`):
|
|
497
|
+
|
|
498
|
+
- `==` - equals (with multiple values = OR)
|
|
499
|
+
- `!=` - not equals (with multiple values = neither A nor B)
|
|
500
|
+
- `exists` / `!exists` - key exists or doesn't exist
|
|
501
|
+
- `is-true` / `is-false` - boolean checks
|
|
502
|
+
- `is-not-true` / `is-not-false` - negated boolean checks
|
|
503
|
+
- `is-empty` / `is-not-empty` - empty value checks
|
|
504
|
+
|
|
505
|
+
**Logic Rules**:
|
|
506
|
+
|
|
507
|
+
- Multiple values in ONE condition = OR
|
|
508
|
+
- Multiple SEPARATE conditions = AND
|
|
509
|
+
- Conditional tags apply tags conditionally based on conditions
|
|
510
|
+
|
|
511
|
+
**Storage**: Conditions are stored in the component/page JSON and preserved in config.json (not in React/CSS/JS files).
|
|
512
|
+
|
|
513
|
+
## Config.json Structure
|
|
514
|
+
|
|
515
|
+
The `config.json` file contains the reduced Embeddable JSON with:
|
|
516
|
+
|
|
517
|
+
- All structural properties (id, title, builder_version, etc.)
|
|
518
|
+
- Page metadata (id, key, properties) but NOT component content (components are in React files)
|
|
519
|
+
- Style selectors but NOT style values (styles are in CSS files)
|
|
520
|
+
- Computed field metadata (id, key, inputs, etc.) but NOT code (code is in JS files)
|
|
521
|
+
- Action metadata (id, name, output, mappings, etc.) but NOT code (code is in JS files)
|
|
522
|
+
- All other Flow properties (experiments, transitions, settings, etc.)
|
|
523
|
+
|
|
524
|
+
**Example config.json structure**:
|
|
525
|
+
|
|
526
|
+
```json
|
|
527
|
+
{
|
|
528
|
+
"id": "flow_123",
|
|
529
|
+
"title": "My Embeddable",
|
|
530
|
+
"builder_version": 4,
|
|
531
|
+
"pages": [
|
|
532
|
+
{
|
|
533
|
+
"id": "page_1",
|
|
534
|
+
"key": "welcome_page",
|
|
535
|
+
"tags": ["intro"],
|
|
536
|
+
"is_exit_page": false
|
|
537
|
+
// components[] not included - stored in pages/welcome_page.page.tsx
|
|
538
|
+
}
|
|
539
|
+
],
|
|
540
|
+
// 'components' property not included - all info stored in `global-components/{locationKey}.location.tsx`
|
|
541
|
+
"styles": {
|
|
542
|
+
".Flow-Component.ComponentTag-primary_button": {}
|
|
543
|
+
// Style values not included - stored in CSS files
|
|
544
|
+
},
|
|
545
|
+
"computedFields": [
|
|
546
|
+
{
|
|
547
|
+
"id": "cf_1",
|
|
548
|
+
"key": "total_price",
|
|
549
|
+
"inputs": ["price", "quantity"]
|
|
550
|
+
// code not included - stored in computed-fields/total_price.js
|
|
551
|
+
}
|
|
552
|
+
],
|
|
553
|
+
"dataOutputs": [
|
|
554
|
+
{
|
|
555
|
+
"id": "action_1",
|
|
556
|
+
"name": "submit_form",
|
|
557
|
+
"output": "custom"
|
|
558
|
+
// code not included - stored in actions/submit_form.js
|
|
559
|
+
}
|
|
560
|
+
]
|
|
561
|
+
}
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
## File Organization Rules
|
|
565
|
+
|
|
566
|
+
### React Files (Pages)
|
|
567
|
+
|
|
568
|
+
- Location: `pages/{pageKey}.page.tsx`
|
|
569
|
+
- Export: **Default export** of React component (e.g., `export default function PageName()`)
|
|
570
|
+
- Props: Component props match JSON properties
|
|
571
|
+
- Keys: All components must have `id` and `key` props matching component `id` and `key` properties respectively
|
|
572
|
+
|
|
573
|
+
### React Files (Global Components)
|
|
574
|
+
|
|
575
|
+
- Location: `global-components/{locationKey}.location.tsx`
|
|
576
|
+
- Export: **Default export** of React component (e.g., `export default function GlobalComponents()`)
|
|
577
|
+
- Props: Component props match JSON properties
|
|
578
|
+
- Location: `_location` property not needed here since the filenames contain the location of that group of components
|
|
579
|
+
|
|
580
|
+
### CSS Files
|
|
581
|
+
|
|
582
|
+
- Location: `styles/{selector}.css` or organized by tag/component
|
|
583
|
+
- Format: Standard CSS with selector as class name
|
|
584
|
+
- Content: Style properties from `Flow.styles[selector]`
|
|
585
|
+
|
|
586
|
+
### JS Files (Computed Fields)
|
|
587
|
+
|
|
588
|
+
- Location: `computed-fields/{key}.js`
|
|
589
|
+
- Export: Function named `result` that returns computed value
|
|
590
|
+
- Access: Global `userData` object available
|
|
591
|
+
|
|
592
|
+
### JS Files (Actions)
|
|
593
|
+
|
|
594
|
+
- Location: `actions/{name}.js`
|
|
595
|
+
- Export: Function named `output` for custom actions
|
|
596
|
+
- Access: Global `userData` object available
|
|
597
|
+
|
|
598
|
+
## Key Principles for Modifications
|
|
599
|
+
|
|
600
|
+
1. **Component Keys**: Must be unique and descriptive. Keys become User Data keys for input components.
|
|
601
|
+
|
|
602
|
+
2. **Tags**: Used for CSS styling. Apply tags consistently:
|
|
603
|
+
- Primary tag (required): Describes component type (e.g., `standard_button`, `price_card`)
|
|
604
|
+
- Secondary tag (optional): Variant of primary (e.g., `primary_button`, `outline_button`)
|
|
605
|
+
- Utility tag (optional): Only for margins that can't be applied via primary/secondary (e.g., `m_2`)
|
|
606
|
+
|
|
607
|
+
3. **Component Types**:
|
|
608
|
+
- Use `PlainText` by default
|
|
609
|
+
- Use `RichTextMarkdown` only for mixed formatting (bold, italic, lists, links, colors)
|
|
610
|
+
- Use `CustomHTML` for any other styling or formatting needs
|
|
611
|
+
|
|
612
|
+
4. **Nested Components**: Containers can have `components[]` array with child components. Render as nested JSX.
|
|
613
|
+
|
|
614
|
+
5. **Global Components**: Must use the filenames to replace the `_location` property.
|
|
615
|
+
|
|
616
|
+
6. **Conditions**: Stored in config.json, applied at runtime. Multiple values in one condition = OR, multiple conditions = AND.
|
|
617
|
+
|
|
618
|
+
7. **CSS Selectors**: Must follow exact structure with proper spacing (trailing space on breakpoints, leading space on sub-elements).
|
|
619
|
+
|
|
620
|
+
8. **User Data Keys**: Input component keys become User Data keys. Choose descriptive names (e.g., `email` not `email_input`).
|
|
621
|
+
|
|
622
|
+
9. **Computed Field Dependencies**: `inputs` array defines which User Data keys trigger recalculation. Can reference other computed fields.
|
|
623
|
+
|
|
624
|
+
10. **Action Triggers**: Actions are triggered by events (button clicks, page completion, etc.). Configuration stored in config.json, code in JS files.
|
|
625
|
+
|
|
626
|
+
## Common Patterns
|
|
627
|
+
|
|
628
|
+
### Adding a Component to a Page
|
|
629
|
+
|
|
630
|
+
1. Update the page's React file (`pages/{pageKey}.page.tsx`)
|
|
631
|
+
2. Add component JSX with appropriate props
|
|
632
|
+
3. If new tags are used, ensure CSS exists for those tags
|
|
633
|
+
4. Update config.json if component metadata is needed (rarely)
|
|
634
|
+
|
|
635
|
+
### Styling a Component
|
|
636
|
+
|
|
637
|
+
1. Identify the component's tag
|
|
638
|
+
2. Construct CSS selector following the selector system
|
|
639
|
+
3. Add/update CSS file with styles
|
|
640
|
+
4. Ensure selector matches the component's structure
|
|
641
|
+
|
|
642
|
+
### Adding a Computed Field
|
|
643
|
+
|
|
644
|
+
1. Create JS file: `computed-fields/{key}.js`
|
|
645
|
+
2. Implement `result()` function
|
|
646
|
+
3. Add metadata to config.json: `computedFields` array
|
|
647
|
+
4. Specify `inputs` array for dependencies
|
|
648
|
+
|
|
649
|
+
### Adding an Action
|
|
650
|
+
|
|
651
|
+
1. Create JS file: `actions/{name}.js`
|
|
652
|
+
2. Implement `output()` function (for custom actions)
|
|
653
|
+
3. Add metadata to config.json: `dataOutputs` array
|
|
654
|
+
4. Configure action type and mappings in config.json
|
|
655
|
+
|
|
656
|
+
### Modifying Global Components
|
|
657
|
+
|
|
658
|
+
1. Update React file: `global-components/{locationKey}.location.tsx`
|
|
659
|
+
2. Update styles if needed
|
|
660
|
+
|
|
661
|
+
## Important Notes
|
|
662
|
+
|
|
663
|
+
- **IDs**: All components, pages, computed fields, actions, and buttons inside OptionSelectors have unique `id` properties. These are preserved in config.json and used for internal references, except for component and button ids which should always be written in React
|
|
664
|
+
|
|
665
|
+
- **Keys**: Component and page `key` properties are used for file naming and must be valid file names (no special characters, use underscores).
|
|
666
|
+
|
|
667
|
+
- **Parent-Child Relationships**: Components can be nested. Parent component has `components[]` array. Child components have `parent_id` property.
|
|
668
|
+
|
|
669
|
+
- **Repeaters**: Components with `repeater_key` repeat based on array data in User Data.
|
|
670
|
+
|
|
671
|
+
- **Conditions**: Applied at runtime. Components/pages with conditions that don't pass are hidden.
|
|
672
|
+
|
|
673
|
+
- **Tags**: Multiple tags can be applied. CSS selectors can target by any tag.
|
|
674
|
+
|
|
675
|
+
- **Breakpoints**: CSS supports responsive design via breakpoint selectors. Default is desktop, mobile/tablet use `[max-width~='{width}px'] ` syntax.
|
|
676
|
+
|
|
677
|
+
- **States**: Interactive elements support states (hover, selected, disabled, etc.). CSS selectors include state syntax.
|
|
678
|
+
|
|
679
|
+
- **Sub-elements**: Components have internal structure. CSS can target sub-elements using `.ElementType-{Type}` syntax.
|