@embeddables/cli 0.1.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.
Files changed (49) hide show
  1. package/.cursor/rules/embeddables-cli.md +679 -0
  2. package/README.md +37 -7
  3. package/dist/auth/index.d.ts +1 -1
  4. package/dist/auth/index.d.ts.map +1 -1
  5. package/dist/auth/index.js +5 -3
  6. package/dist/cli.js +42 -6
  7. package/dist/commands/branch.d.ts +4 -0
  8. package/dist/commands/branch.d.ts.map +1 -0
  9. package/dist/commands/branch.js +46 -0
  10. package/dist/commands/dev.d.ts +1 -1
  11. package/dist/commands/dev.d.ts.map +1 -1
  12. package/dist/commands/dev.js +48 -9
  13. package/dist/commands/init.d.ts +5 -0
  14. package/dist/commands/init.d.ts.map +1 -0
  15. package/dist/commands/init.js +245 -0
  16. package/dist/commands/pull.d.ts +1 -1
  17. package/dist/commands/pull.d.ts.map +1 -1
  18. package/dist/commands/pull.js +104 -3
  19. package/dist/commands/save.d.ts +8 -0
  20. package/dist/commands/save.d.ts.map +1 -0
  21. package/dist/commands/save.js +269 -0
  22. package/dist/compiler/index.d.ts.map +1 -1
  23. package/dist/compiler/index.js +4 -0
  24. package/dist/compiler/reverse.d.ts.map +1 -1
  25. package/dist/compiler/reverse.js +4 -10
  26. package/dist/config/index.d.ts +23 -0
  27. package/dist/config/index.d.ts.map +1 -0
  28. package/dist/config/index.js +42 -0
  29. package/dist/constants.d.ts +7 -0
  30. package/dist/constants.d.ts.map +1 -0
  31. package/dist/constants.js +6 -0
  32. package/dist/helpers/dates.d.ts +5 -0
  33. package/dist/helpers/dates.d.ts.map +1 -0
  34. package/dist/helpers/dates.js +7 -0
  35. package/dist/prompts/branches.d.ts +20 -0
  36. package/dist/prompts/branches.d.ts.map +1 -0
  37. package/dist/prompts/branches.js +89 -0
  38. package/dist/prompts/embeddables.d.ts +41 -0
  39. package/dist/prompts/embeddables.d.ts.map +1 -0
  40. package/dist/prompts/embeddables.js +161 -0
  41. package/dist/prompts/index.d.ts +7 -0
  42. package/dist/prompts/index.d.ts.map +1 -0
  43. package/dist/prompts/index.js +4 -0
  44. package/dist/prompts/projects.d.ts +22 -0
  45. package/dist/prompts/projects.d.ts.map +1 -0
  46. package/dist/prompts/projects.js +85 -0
  47. package/dist/proxy/server.d.ts.map +1 -1
  48. package/dist/proxy/server.js +10 -4
  49. package/package.json +6 -5
@@ -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.