@embeddables/cli 0.14.4 → 0.15.0-beta.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/.prompts/custom/build-funnel.md +3 -1
- package/.prompts/custom/carousel.md +159 -0
- package/.prompts/embeddables-cli.md +116 -42
- package/.prompts/short-rule-body.md +15 -0
- package/README.md +94 -0
- package/dist/auth/index.d.ts +27 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +44 -0
- package/dist/auth/index.js.map +1 -1
- package/dist/cli.js +69 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/build-workbench.d.ts +5 -0
- package/dist/commands/build-workbench.d.ts.map +1 -0
- package/dist/commands/build-workbench.js +117 -0
- package/dist/commands/build-workbench.js.map +1 -0
- package/dist/commands/dangerously-publish.d.ts +53 -0
- package/dist/commands/dangerously-publish.d.ts.map +1 -0
- package/dist/commands/dangerously-publish.js +731 -0
- package/dist/commands/dangerously-publish.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +3 -253
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/login.d.ts +2 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +41 -1
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/provide-otp.d.ts +11 -0
- package/dist/commands/provide-otp.d.ts.map +1 -0
- package/dist/commands/provide-otp.js +102 -0
- package/dist/commands/provide-otp.js.map +1 -0
- package/dist/commands/update-project-files.d.ts +8 -0
- package/dist/commands/update-project-files.d.ts.map +1 -0
- package/dist/commands/update-project-files.js +245 -0
- package/dist/commands/update-project-files.js.map +1 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +16 -0
- package/dist/commands/upgrade.js.map +1 -1
- package/dist/compiler/flatten.js +1 -0
- package/dist/compiler/parsePage.js +14 -9
- package/dist/compiler/parsePage.js.map +1 -1
- package/dist/compiler/reverse.d.ts.map +1 -1
- package/dist/compiler/reverse.js +18 -8
- package/dist/compiler/reverse.js.map +1 -1
- package/dist/components/primitives/CustomHTML.d.ts +2 -1
- package/dist/components/primitives/CustomHTML.d.ts.map +1 -1
- package/dist/components/primitives/CustomHTML.js +11 -2
- package/dist/components/primitives/CustomHTML.js.map +1 -1
- package/dist/components/primitives/OptionSelector.d.ts +1 -0
- package/dist/components/primitives/OptionSelector.d.ts.map +1 -1
- package/dist/components/primitives/OptionSelector.js.map +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -3
- package/dist/constants.js.map +1 -1
- package/dist/helpers/TEMP helpers file.d.ts +1 -0
- package/dist/helpers/TEMP helpers file.d.ts.map +1 -0
- package/dist/helpers/TEMP helpers file.js +1 -0
- package/dist/helpers/json.d.ts.map +1 -1
- package/dist/helpers/json.js +132 -17
- package/dist/helpers/json.js.map +1 -1
- package/dist/prompts/branches.d.ts.map +1 -1
- package/dist/prompts/branches.js +2 -0
- package/dist/prompts/branches.js.map +1 -1
- package/dist/types-builder.d.ts +6 -2
- package/dist/types-builder.d.ts.map +1 -1
- package/dist/workbench/AutofillPanel.d.ts.map +1 -1
- package/dist/workbench/AutofillPanel.js +52 -14
- package/dist/workbench/AutofillPanel.js.map +1 -1
- package/dist/workbench/FeedbackPanel.d.ts +39 -0
- package/dist/workbench/FeedbackPanel.d.ts.map +1 -0
- package/dist/workbench/FeedbackPanel.js +279 -0
- package/dist/workbench/FeedbackPanel.js.map +1 -0
- package/dist/workbench/PageThumbnailStrip.d.ts +6 -0
- package/dist/workbench/PageThumbnailStrip.d.ts.map +1 -0
- package/dist/workbench/PageThumbnailStrip.js +124 -0
- package/dist/workbench/PageThumbnailStrip.js.map +1 -0
- package/dist/workbench/Toast.d.ts +18 -0
- package/dist/workbench/Toast.d.ts.map +1 -0
- package/dist/workbench/Toast.js +46 -0
- package/dist/workbench/Toast.js.map +1 -0
- package/dist/workbench/UserDataPanel.d.ts +2 -1
- package/dist/workbench/UserDataPanel.d.ts.map +1 -1
- package/dist/workbench/UserDataPanel.js +2 -1
- package/dist/workbench/UserDataPanel.js.map +1 -1
- package/dist/workbench/WorkbenchApp.d.ts.map +1 -1
- package/dist/workbench/WorkbenchApp.js +19 -3
- package/dist/workbench/WorkbenchApp.js.map +1 -1
- package/dist/workbench/cloudflare-worker/README.md +31 -0
- package/dist/workbench/cloudflare-worker/public/workbench.css +1614 -0
- package/dist/workbench/cloudflare-worker/public/workbench.js +77 -0
- package/dist/workbench/cloudflare-worker/worker.js +40 -0
- package/dist/workbench/cloudflare-worker/wrangler.toml +10 -0
- package/dist/workbench/supabase-browser.d.ts +11 -0
- package/dist/workbench/supabase-browser.d.ts.map +1 -0
- package/dist/workbench/supabase-browser.js +18 -0
- package/dist/workbench/supabase-browser.js.map +1 -0
- package/dist/workbench/types.d.ts +25 -0
- package/dist/workbench/types.d.ts.map +1 -0
- package/dist/workbench/types.js +2 -0
- package/dist/workbench/types.js.map +1 -0
- package/dist/workbench/useComponentSelection.d.ts +27 -0
- package/dist/workbench/useComponentSelection.d.ts.map +1 -0
- package/dist/workbench/useComponentSelection.js +203 -0
- package/dist/workbench/useComponentSelection.js.map +1 -0
- package/dist/workbench/workbench.css +1614 -0
- package/dist/workbench/workbench.js +77 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ We need to build the pages from the attached images. Please create a plan and hi
|
|
|
2
2
|
|
|
3
3
|
Embeddable ID: <EMBEDDABLE_ID>
|
|
4
4
|
|
|
5
|
-
Important: Always read the Embeddables CLI context before starting (e.g. @.cursor/rules/embeddables-cli.md, @.claude/
|
|
5
|
+
Important: Always read the Embeddables CLI context before starting. The full guide is at @embeddables/AI-README.md. Your editor's rule file (e.g. @.cursor/rules/embeddables-cli.md, @.claude/CLAUDE.md, @AGENTS.md, or @.agent/rules/embeddables-cli.md) points to it with a summary of what it covers.
|
|
6
6
|
|
|
7
7
|
[KEEP / REMOVE]
|
|
8
8
|
Use the designs from the images but use the content from the attached document instead, and build out the pages based on that content. Therefore, when using the designs from the images, for each page just find the most relevant design for the page's content and design it based on that. However, don't be confined to the provided designs - just find the closest design and modify it to fit the content required.
|
|
@@ -17,6 +17,8 @@ Notes:
|
|
|
17
17
|
a. `emojiIcon` with an emoji when there is an appropriate one (can be used in `imageUrl` in CustomButton, or `imageUrl` inside OptionSelector buttons)
|
|
18
18
|
b. placeholder image: https://placehold.co/600x400?text=My+Text+To+Display if there is text in the image (can't be emojis), otherwise https://placehold.co/600x400 (but with the correct size specified in the URL in either case; can be used in `src` in MediaImage, `imageUrl` in CustomButton, or `imageUrl` inside OptionSelector buttons)
|
|
19
19
|
|
|
20
|
+
Before finishing: **Review** your changes per the Embeddables CLI rules—especially the "When to Review (Required Checks)" section. Run `embeddables build` to verify compilation, then run `embeddables save` and review the JSON diff it shows before confirming.
|
|
21
|
+
|
|
20
22
|
At the end of your work, include a short summary covering:
|
|
21
23
|
|
|
22
24
|
- Decisions made – Any implementation choices where requirements, design, or instructions were not fully explicit.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Carousel / Swiper Implementation Guide
|
|
2
|
+
|
|
3
|
+
When building a carousel or slider in an Embeddable, use **Swiper v11.2.10** loaded at runtime via CDN. No build tooling is required.
|
|
4
|
+
|
|
5
|
+
## Pattern Overview
|
|
6
|
+
|
|
7
|
+
1. **Page TSX structure** — A `Container` with child elements (each child becomes a slide)
|
|
8
|
+
2. **Initialization action** — An action file that loads Swiper from CDN and initializes it on `outputs_onloadflow`
|
|
9
|
+
3. **CSS** — Styles for the carousel container and optional prev/next navigation buttons
|
|
10
|
+
4. **Optional** — Prev/next `CustomButton` components for navigation
|
|
11
|
+
|
|
12
|
+
## 1. Page TSX Structure
|
|
13
|
+
|
|
14
|
+
Structure your carousel as nested Containers:
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
<Container id="comp_xxx" key="carousel_container" tags={["carousel_container"]}>
|
|
18
|
+
{/* Inner container — this becomes the Swiper container (identified by key) */}
|
|
19
|
+
<Container id="comp_yyy" key="my_carousel" tags={["carousel_inner"]}>
|
|
20
|
+
{/* Each direct child becomes a slide */}
|
|
21
|
+
<Container id="comp_1" key="slide_1" tags={["slide"]}>...</Container>
|
|
22
|
+
<Container id="comp_2" key="slide_2" tags={["slide"]}>...</Container>
|
|
23
|
+
<Container id="comp_3" key="slide_3" tags={["slide"]}>...</Container>
|
|
24
|
+
</Container>
|
|
25
|
+
{/* Optional prev/next buttons */}
|
|
26
|
+
<CustomButton id="comp_prev" key="carousel_prev" tags={["carousel_arrow", "left_arrow"]} text="" action="no-action" />
|
|
27
|
+
<CustomButton id="comp_next" key="carousel_next" tags={["carousel_arrow", "right_arrow"]} text="" action="no-action" />
|
|
28
|
+
</Container>
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
- The **inner** `Container` (e.g. `key="my_carousel"`) is the one the action targets — it must have a unique `key`.
|
|
32
|
+
- The action adds `swiper`, `swiper-wrapper`, and `swiper-slide` classes at runtime.
|
|
33
|
+
- Slides can be any component: `Container`, `MediaImage`, `CustomHTML`, etc.
|
|
34
|
+
|
|
35
|
+
## 2. Action File
|
|
36
|
+
|
|
37
|
+
Create an action (e.g. `actions/swiper.js`) that:
|
|
38
|
+
|
|
39
|
+
1. Loads Swiper CSS and JS from CDN
|
|
40
|
+
2. Finds the container by `data-component-key-value` (the component `key`)
|
|
41
|
+
3. Adds Swiper classes to the container and its children
|
|
42
|
+
4. Initializes `new Swiper(container, options)` with optional navigation
|
|
43
|
+
|
|
44
|
+
**Minimal template:**
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
function output(userData) {
|
|
48
|
+
loadStylesheet("https://unpkg.com/swiper@11.2.10/swiper-bundle.min.css");
|
|
49
|
+
loadScript("https://unpkg.com/swiper@11.2.10/swiper-bundle.min.js", () => {
|
|
50
|
+
initCarousel({
|
|
51
|
+
containerKey: "my_carousel", // key of the inner Container
|
|
52
|
+
prevKey: "carousel_prev", // key of prev button (or null)
|
|
53
|
+
nextKey: "carousel_next", // key of next button (or null)
|
|
54
|
+
slidesPerView: 1,
|
|
55
|
+
spaceBetween: 10,
|
|
56
|
+
loop: true,
|
|
57
|
+
breakpoints: {
|
|
58
|
+
1024: { slidesPerView: 3, spaceBetween: 24 },
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function loadStylesheet(href) {
|
|
65
|
+
const link = document.createElement("link");
|
|
66
|
+
link.rel = "stylesheet";
|
|
67
|
+
link.href = href;
|
|
68
|
+
document.head.appendChild(link);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function loadScript(src, callback) {
|
|
72
|
+
const script = document.createElement("script");
|
|
73
|
+
script.src = src;
|
|
74
|
+
script.onload = callback;
|
|
75
|
+
document.head.appendChild(script);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function getElementByKey(key) {
|
|
79
|
+
return document.querySelector(`[data-component-key-value="${key}"]`);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function initCarousel(options) {
|
|
83
|
+
const { containerKey, prevKey, nextKey, ...swiperOptions } = options;
|
|
84
|
+
const container = getElementByKey(containerKey);
|
|
85
|
+
if (!container) return;
|
|
86
|
+
|
|
87
|
+
const wrapper = container.firstElementChild;
|
|
88
|
+
if (!wrapper) return;
|
|
89
|
+
|
|
90
|
+
wrapper.classList.add("swiper-wrapper");
|
|
91
|
+
Array.from(wrapper.children).forEach(el => el.classList.add("swiper-slide"));
|
|
92
|
+
container.classList.add("swiper");
|
|
93
|
+
|
|
94
|
+
const nextBtn = nextKey ? getElementByKey(nextKey) : null;
|
|
95
|
+
const prevBtn = prevKey ? getElementByKey(prevKey) : null;
|
|
96
|
+
|
|
97
|
+
new Swiper(container, {
|
|
98
|
+
...swiperOptions,
|
|
99
|
+
navigation: nextBtn && prevBtn ? { nextEl: nextBtn, prevEl: prevBtn } : {},
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Wire the action** to `outputs_onloadflow` in `config.json` so it runs when the embeddable loads.
|
|
105
|
+
|
|
106
|
+
## 3. CSS
|
|
107
|
+
|
|
108
|
+
Add styles for the carousel layout and navigation buttons in `styles/index.css`:
|
|
109
|
+
|
|
110
|
+
```css
|
|
111
|
+
.Flow-Component.ComponentTag-carousel_container {
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
width: 100%;
|
|
115
|
+
position: relative;
|
|
116
|
+
justify-content: center;
|
|
117
|
+
height: fit-content;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.Flow-Component.ComponentTag-carousel_arrow {
|
|
121
|
+
width: 44px;
|
|
122
|
+
height: 44px;
|
|
123
|
+
z-index: 10;
|
|
124
|
+
position: absolute;
|
|
125
|
+
cursor: pointer;
|
|
126
|
+
background-size: contain;
|
|
127
|
+
background-position: center;
|
|
128
|
+
border-radius: 50%;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
.Flow-Component.ComponentTag-left_arrow {
|
|
132
|
+
left: -72px;
|
|
133
|
+
/* background-image: url(...) for arrow icon */
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.Flow-Component.ComponentTag-right_arrow {
|
|
137
|
+
right: -72px;
|
|
138
|
+
/* background-image: url(...) for arrow icon */
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Use breakpoints to adjust arrow position on mobile (e.g. overlay on the carousel).
|
|
143
|
+
|
|
144
|
+
## 4. Swiper Options
|
|
145
|
+
|
|
146
|
+
Swiper options are passed to `new Swiper(container, options)`. Common options:
|
|
147
|
+
|
|
148
|
+
- `slidesPerView` — Number of visible slides
|
|
149
|
+
- `spaceBetween` — Gap between slides (px)
|
|
150
|
+
- `loop` — Infinite loop
|
|
151
|
+
- `autoplay` — Auto-advance (object with `delay`, etc.)
|
|
152
|
+
- `breakpoints` — Responsive overrides (see [Swiper API](https://swiperjs.com/swiper-api))
|
|
153
|
+
|
|
154
|
+
## Key Points
|
|
155
|
+
|
|
156
|
+
- **Swiper v11.2.10** is loaded at runtime via CDN — no npm install or build step.
|
|
157
|
+
- Turn any `Container` with child elements into a carousel via an action that loads and initializes Swiper.
|
|
158
|
+
- The container is identified by its component `key` (exposed as `data-component-key-value` in the DOM).
|
|
159
|
+
- Do not add `swiper`, `swiper-wrapper`, or `swiper-slide` classes in TSX — the action adds them at runtime.
|
|
@@ -20,6 +20,23 @@ The CLI transforms an Embeddable JSON into:
|
|
|
20
20
|
- **JS files**: Computed fields and actions
|
|
21
21
|
- **config.json**: Reduced JSON containing structure/metadata without file content
|
|
22
22
|
|
|
23
|
+
## When to Review (Required Checks)
|
|
24
|
+
|
|
25
|
+
Review is **required** in these situations. Do not skip these checks.
|
|
26
|
+
|
|
27
|
+
| When (trigger) | Where to review | What to verify |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| **Before running `embeddables save`** | The JSON diff shown by the save command | Confirm the diff matches your intended changes. Do not save without reviewing—unexpected additions, removals, or edits may indicate a compile or config error. |
|
|
30
|
+
| **After modifying `config.json`** | `config.json` and any related React/JS files | Ensure `pages[]`, `computedFields`, `dataOutputs`, `breakpoints`, and other structure match the actual page files, computed-field files, and action files. IDs and keys must be consistent. |
|
|
31
|
+
| **After adding or removing pages** | `config.json` → `pages[]` and `pages/{pageKey}.page.tsx` | Each page in `config.json` must have a matching `.page.tsx` file, and vice versa. |
|
|
32
|
+
| **After adding or removing components** | Page React files and `config.json` | Component IDs and keys must be unique. Nested components need correct `parent_id`. |
|
|
33
|
+
| **After adding or removing actions or computed fields** | `config.json` → `dataOutputs` / `computedFields` and `actions/` / `computed-fields/` | Each entry in config must have a matching JS file; each JS file must have a config entry. |
|
|
34
|
+
| **After changing action triggers** | `config.json` (flow/page level) and component props (React) | `outputs_on*` fields must reference valid action IDs. Page-level triggers use arrays; flow-level can be string or array. |
|
|
35
|
+
| **After modifying styles** | `styles/index.css` and the JSON diff from `embeddables save` | Confirm no unexpected selectors were added or removed; check that breakpoint names match `config.json`. |
|
|
36
|
+
| **When debugging round-trip or compile issues** | `embeddables inspect --bypass-auth --id <embeddableId>` output | Run inspect to compare original vs rebuilt JSON and identify divergences. |
|
|
37
|
+
|
|
38
|
+
**Summary**: A review is required whenever you add, remove, or modify pages, components, styles, actions, computed fields, or config. In those cases, review the affected files and the JSON diff before saving.
|
|
39
|
+
|
|
23
40
|
## Embeddable JSON Structure (Flow)
|
|
24
41
|
|
|
25
42
|
The root Embeddable object (called `Flow`) is defined in `src/types-builder.ts`. Key properties:
|
|
@@ -413,6 +430,22 @@ NOTE: OptionSelectors are either dropdowns (`dropdown` is `true`, can use other
|
|
|
413
430
|
Note: the component itself IS the button. There is no `button` element inside it.
|
|
414
431
|
Note: button icons and checkboxes are before text in the button, unless you set the flex-direction to be row-reverse or column-reverse.
|
|
415
432
|
|
|
433
|
+
**Making a CTA disabled until validation passes**: Use `needs_validation_passed` together with `validation_show_state`. Both props must be set — `needs_validation_passed` alone does not visually disable the button.
|
|
434
|
+
|
|
435
|
+
```tsx
|
|
436
|
+
<CustomButton
|
|
437
|
+
needs_validation_passed={true}
|
|
438
|
+
validation_show_state="disable"
|
|
439
|
+
{/* ... other props ... */}
|
|
440
|
+
/>
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
- `needs_validation_passed={true}` — prevents the button action from firing until all validation on the page passes.
|
|
444
|
+
- `validation_show_state` — controls how the button appears when validation has not yet passed:
|
|
445
|
+
- `"disable"` — button appears disabled (grayed out, not clickable). **Use this for the standard "disabled until valid" CTA pattern.**
|
|
446
|
+
- `"hide"` — button is hidden entirely until validation passes.
|
|
447
|
+
- `"always_show"` — button always appears enabled regardless of validation state (default behaviour; omit both props if this is what you want).
|
|
448
|
+
|
|
416
449
|
| ElementType | Element | Condition | Notes |
|
|
417
450
|
| ------------------- | ---------------- | ------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
418
451
|
| `ButtonTextWrapper` | `<div>` | Always | |
|
|
@@ -488,6 +521,8 @@ NOTE: there is also a reset icon button with the class `reset` - hide this if yo
|
|
|
488
521
|
|
|
489
522
|
## Computed Fields Structure
|
|
490
523
|
|
|
524
|
+
> **Docs**: [Computed Fields](https://docs.embeddables.com/features/computed-fields) · [Custom Code](https://docs.embeddables.com/guides/custom-code)
|
|
525
|
+
|
|
491
526
|
Computed fields are stored in `Flow.computedFields` and transform to JS files. The `ComputedField` type is defined in `src/types-builder.ts`.
|
|
492
527
|
|
|
493
528
|
**Key Properties**:
|
|
@@ -500,28 +535,42 @@ Computed fields are stored in `Flow.computedFields` and transform to JS files. T
|
|
|
500
535
|
|
|
501
536
|
**File Mapping**: `computed-fields/{key}.js` (e.g., `computed-fields/total_price.js`)
|
|
502
537
|
|
|
503
|
-
**Code Structure**: The `code` property contains JavaScript that must include a function called `result`. The runtime passes
|
|
538
|
+
**Code Structure**: The `code` property contains JavaScript that must include a function called `result`. The runtime passes three arguments — `userData`, `helperFunctions`, and `triggerContext`:
|
|
504
539
|
|
|
505
540
|
```javascript
|
|
506
|
-
function result(
|
|
507
|
-
// Access user data via global userData object
|
|
541
|
+
function result(userData, helperFunctions, triggerContext) {
|
|
508
542
|
const price = userData.price || 0
|
|
509
543
|
const quantity = userData.quantity || 0
|
|
510
544
|
|
|
511
|
-
//
|
|
512
|
-
//
|
|
545
|
+
// helperFunctions.trackCustomEvent(name, props) can track analytics events
|
|
546
|
+
// triggerContext contains metadata about what triggered this recalculation
|
|
513
547
|
|
|
514
548
|
return price * quantity
|
|
515
549
|
}
|
|
516
550
|
```
|
|
517
551
|
|
|
552
|
+
**Function arguments**:
|
|
553
|
+
|
|
554
|
+
| Argument | Description |
|
|
555
|
+
| --- | --- |
|
|
556
|
+
| `userData` | Object containing the User Data fields listed in **Inputs** (or all User Data when "Include full User Data" is enabled — see below). The computed field's own key is excluded to prevent circular references. |
|
|
557
|
+
| `helperFunctions` | Object with helper utilities. Currently provides `trackCustomEvent(eventName, eventProps)` for analytics (see "Custom Event Tracking" below). |
|
|
558
|
+
| `triggerContext` | Metadata about what triggered the recalculation (see "Trigger Context" below). For computed fields the shape differs slightly from actions — it describes which input changed rather than a UI event. |
|
|
559
|
+
|
|
560
|
+
**Scope of `userData` and when it re-runs**:
|
|
561
|
+
|
|
562
|
+
Computed fields can be configured (in the builder UI) to receive broader User Data, which affects both what's available inside `result(userData, …)` and when it re-runs:
|
|
563
|
+
|
|
564
|
+
- **Include and watch full User Data** — `userData` contains the entire User Data object (excluding the computed field's own key). The field re-runs whenever *any* User Data value changes. Useful when a field needs access to everything or should update frequently.
|
|
565
|
+
- **Include, but not watch full User Data** — `userData` contains the entire User Data object (excluding the computed field's own key), but the field only re-runs when the keys listed in **Inputs** or **Triggers** change. Useful when full access is needed but recalculation should be controlled.
|
|
566
|
+
- **Default (neither option)** — `userData` contains only the keys listed in **Inputs**. The field re-runs when those keys (or any **Triggers**) change.
|
|
567
|
+
|
|
518
568
|
**Important**:
|
|
519
569
|
|
|
570
|
+
- **Always write at least `function result(userData)`, never `function result()`.** Omit trailing parameters you don't use: `result(userData)` if you only need user data, `result(userData, helperFunctions)` if you also need helpers, or `result(userData, helperFunctions, triggerContext)` if you need all three. You cannot skip a parameter to reach a later one (e.g. you must include `helperFunctions` to access `triggerContext`).
|
|
520
571
|
- Computed fields can reference other computed fields via their keys in `inputs`
|
|
521
|
-
- The function has access to a global `userData` object containing all user data
|
|
522
572
|
- If `async` is true, the function can return a Promise
|
|
523
573
|
- `input_triggers` specify additional keys that trigger recalculation
|
|
524
|
-
- The `context` parameter provides `triggerContext` and `trackCustomEvent()` — same as actions (see "Trigger Context" and "Custom Event Tracking" under Actions)
|
|
525
574
|
|
|
526
575
|
## Actions Structure
|
|
527
576
|
|
|
@@ -536,19 +585,18 @@ Actions are stored in `Flow.dataOutputs` and transform to JS files. The `Action`
|
|
|
536
585
|
|
|
537
586
|
**File Mapping**: `actions/{name}.js` (e.g., `actions/submit_form.js`)
|
|
538
587
|
|
|
539
|
-
**Code Structure**: For `output: "custom"`, the `code` property contains JavaScript that must include a function called `output`. The runtime passes
|
|
588
|
+
**Code Structure**: For `output: "custom"`, the `code` property contains JavaScript that must include a function called `output`. The runtime passes three arguments — `userData`, `helperFunctions`, and `triggerContext`:
|
|
540
589
|
|
|
541
590
|
```javascript
|
|
542
|
-
function output(
|
|
543
|
-
// Access user data via global userData object
|
|
591
|
+
function output(userData, helperFunctions, triggerContext) {
|
|
544
592
|
const email = userData.email
|
|
545
593
|
const name = userData.name
|
|
546
594
|
|
|
547
|
-
//
|
|
548
|
-
const
|
|
595
|
+
// triggerContext contains metadata about what fired this action (see below)
|
|
596
|
+
const { triggerType, pageKey } = triggerContext || {}
|
|
549
597
|
|
|
550
|
-
// Track a custom analytics event
|
|
551
|
-
|
|
598
|
+
// Track a custom analytics event
|
|
599
|
+
helperFunctions.trackCustomEvent('form_submitted', { email })
|
|
552
600
|
|
|
553
601
|
// Perform action (API call, data transformation, etc.)
|
|
554
602
|
return {
|
|
@@ -558,11 +606,18 @@ function output(context) {
|
|
|
558
606
|
}
|
|
559
607
|
```
|
|
560
608
|
|
|
609
|
+
**Function arguments**:
|
|
610
|
+
|
|
611
|
+
| Argument | Description |
|
|
612
|
+
| --- | --- |
|
|
613
|
+
| `userData` | Object containing all User Data at the time the action fires. |
|
|
614
|
+
| `helperFunctions` | Object with helper utilities. Currently provides `trackCustomEvent(eventName, eventProps)` for analytics (see "Custom Event Tracking" below). |
|
|
615
|
+
| `triggerContext` | Metadata about what triggered the action — see "Trigger Context" below. |
|
|
616
|
+
|
|
561
617
|
**Important**:
|
|
562
618
|
|
|
619
|
+
- **Always write at least `function output(userData)`, never `function output()`.** Omit trailing parameters you don't use: `output(userData)` if you only need user data, `output(userData, helperFunctions)` if you also need helpers, or `output(userData, helperFunctions, triggerContext)` if you need all three. You cannot skip a parameter to reach a later one (e.g. you must include `helperFunctions` to access `triggerContext`).
|
|
563
620
|
- Actions are triggered by `outputs_on*` fields (see "Action Trigger Wiring" below) or programmatically
|
|
564
|
-
- The function has access to a global `userData` object
|
|
565
|
-
- The `context` parameter provides `triggerContext` and `trackCustomEvent` (see below)
|
|
566
621
|
- For non-custom actions (airtable, hubspot, etc.), the action configuration is stored in config.json, not in the JS file
|
|
567
622
|
|
|
568
623
|
### Action Trigger Wiring (`outputs_on*` Fields)
|
|
@@ -585,12 +640,14 @@ These are top-level properties on the `Flow` object (stored in `config.json`):
|
|
|
585
640
|
|
|
586
641
|
These are properties on `FlowPage` objects (stored in the page entry in `config.json`):
|
|
587
642
|
|
|
588
|
-
| Field | Type
|
|
589
|
-
| ------------------------- |
|
|
590
|
-
| `outputs_oncomplete` | `string
|
|
591
|
-
| `outputs_onload` | `string
|
|
592
|
-
| `outputs_onopen_infobox` | `string[]`
|
|
593
|
-
| `outputs_onclose_infobox` | `string[]`
|
|
643
|
+
| Field | Type | Fires when |
|
|
644
|
+
| ------------------------- | ---------- | ---------------------------------------------------------- |
|
|
645
|
+
| `outputs_oncomplete` | `string[]` | The user completes this page (i.e. moves to the next page) |
|
|
646
|
+
| `outputs_onload` | `string[]` | The page is loaded / navigated to |
|
|
647
|
+
| `outputs_onopen_infobox` | `string[]` | An info box on this page is opened |
|
|
648
|
+
| `outputs_onclose_infobox` | `string[]` | An info box on this page is closed |
|
|
649
|
+
|
|
650
|
+
**Note**: Page-level trigger fields must be arrays of action IDs. The runtime does not accept a single string for these fields.
|
|
594
651
|
|
|
595
652
|
#### Component-Level Triggers
|
|
596
653
|
|
|
@@ -677,7 +734,9 @@ window.Savvy.triggerAction(embeddableId, actionId)
|
|
|
677
734
|
|
|
678
735
|
### Trigger Context (`triggerContext`)
|
|
679
736
|
|
|
680
|
-
|
|
737
|
+
`triggerContext` is the **third argument** passed to both `output(userData, helperFunctions, triggerContext)` and `result(userData, helperFunctions, triggerContext)`. It contains metadata about what triggered the action or computed field recalculation.
|
|
738
|
+
|
|
739
|
+
**Action `triggerContext` fields**:
|
|
681
740
|
|
|
682
741
|
| Field | Type | Description |
|
|
683
742
|
| -------------- | --------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -690,8 +749,8 @@ When the engine fires an action, it passes a `context` object to the `output(con
|
|
|
690
749
|
Example usage inside an action:
|
|
691
750
|
|
|
692
751
|
```javascript
|
|
693
|
-
function output(
|
|
694
|
-
const { triggerType, componentKey, pageKey } =
|
|
752
|
+
function output(userData, helperFunctions, triggerContext) {
|
|
753
|
+
const { triggerType, componentKey, pageKey } = triggerContext || {}
|
|
695
754
|
|
|
696
755
|
if (triggerType === 'oncomplete' && pageKey === 'checkout_page') {
|
|
697
756
|
// Only run full submission on checkout page completion
|
|
@@ -703,7 +762,7 @@ function output(context) {
|
|
|
703
762
|
}
|
|
704
763
|
```
|
|
705
764
|
|
|
706
|
-
Computed fields also receive `
|
|
765
|
+
**Computed field `triggerContext`**: Computed fields also receive `triggerContext` as the third argument. The shape differs from actions — it describes which input key changed rather than a UI event. This is useful for computed fields that need to behave differently based on what triggered their recalculation.
|
|
707
766
|
|
|
708
767
|
### Custom Event Tracking
|
|
709
768
|
|
|
@@ -711,14 +770,14 @@ Embeddables support tracking custom analytics events. There are two contexts whe
|
|
|
711
770
|
|
|
712
771
|
#### 1. Inside Action or Computed Field Code (Runtime Context)
|
|
713
772
|
|
|
714
|
-
Use `
|
|
773
|
+
Use `helperFunctions.trackCustomEvent(eventName, eventProps)` inside `output()` or `result()` functions:
|
|
715
774
|
|
|
716
775
|
```javascript
|
|
717
|
-
function output(
|
|
776
|
+
function output(userData, helperFunctions, triggerContext) {
|
|
718
777
|
const plan = userData.selected_plan
|
|
719
778
|
|
|
720
779
|
// Track a custom event from within an action
|
|
721
|
-
|
|
780
|
+
helperFunctions.trackCustomEvent('plan_selected', {
|
|
722
781
|
plan: plan,
|
|
723
782
|
price: userData.plan_price,
|
|
724
783
|
})
|
|
@@ -744,10 +803,10 @@ window.Savvy.trackCustomEvent('flow_abc123', 'external_checkout_complete', {
|
|
|
744
803
|
|
|
745
804
|
#### When to Use Which
|
|
746
805
|
|
|
747
|
-
| Context | API
|
|
748
|
-
| ------------------------------ |
|
|
749
|
-
| Inside `output()` / `result()` | `
|
|
750
|
-
| Host page JS | `window.Savvy.trackCustomEvent(flowId, name, props)`
|
|
806
|
+
| Context | API | Use when |
|
|
807
|
+
| ------------------------------ | ------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
808
|
+
| Inside `output()` / `result()` | `helperFunctions.trackCustomEvent(name, props)` | Tracking events as part of action/computed field logic (e.g. after a form submission, when a computed value changes) |
|
|
809
|
+
| Host page JS | `window.Savvy.trackCustomEvent(flowId, name, props)` | Tracking events from outside the embeddable (e.g. after an external checkout, on a parent SPA route change, from a third-party integration callback) |
|
|
751
810
|
|
|
752
811
|
## Conditions Structure
|
|
753
812
|
|
|
@@ -885,14 +944,12 @@ The `_docs` property (string only) documents non-obvious aspects of the embeddab
|
|
|
885
944
|
### JS Files (Computed Fields)
|
|
886
945
|
|
|
887
946
|
- Location: `computed-fields/{key}.js`
|
|
888
|
-
- Export: Function named `result` that returns computed value
|
|
889
|
-
- Access: Global `userData` object available; `context` parameter provides `triggerContext` and `trackCustomEvent()`
|
|
947
|
+
- Export: Function named `result` that returns computed value — signature is `result(userData)`, `result(userData, helperFunctions)`, or `result(userData, helperFunctions, triggerContext)` depending on which arguments are needed (include only the ones you use; trailing unused parameters can be omitted)
|
|
890
948
|
|
|
891
949
|
### JS Files (Actions)
|
|
892
950
|
|
|
893
951
|
- Location: `actions/{name}.js`
|
|
894
|
-
- Export: Function named `output` for custom actions
|
|
895
|
-
- Access: Global `userData` object available; `context` parameter provides `triggerContext` and `trackCustomEvent()`
|
|
952
|
+
- Export: Function named `output` for custom actions — signature is `output(userData)`, `output(userData, helperFunctions)`, or `output(userData, helperFunctions, triggerContext)` depending on which arguments are needed (include only the ones you use; trailing unused parameters can be omitted)
|
|
896
953
|
|
|
897
954
|
## Key Principles for Modifications
|
|
898
955
|
|
|
@@ -931,8 +988,20 @@ The `_docs` property (string only) documents non-obvious aspects of the embeddab
|
|
|
931
988
|
|
|
932
989
|
13. **Asset URL Source of Truth**: If `assets.json` exists in the project root, use it as the first source for image/file URLs. Prefer matching by `flow_id` (or `flow_id: null` for project-level shared assets) and use `relativePath` when mapping local files to uploaded URLs. Always use the (uploaded) url property, not local relative paths, as the image src when building in React.
|
|
933
990
|
|
|
991
|
+
14. **Review before save**: When any change is made (pages, components, styles, config, actions, computed fields), follow "When to Review (Required Checks)" above. Always review the JSON diff before running `embeddables save`.
|
|
992
|
+
|
|
934
993
|
## Common Patterns
|
|
935
994
|
|
|
995
|
+
### Carousel / Swiper
|
|
996
|
+
|
|
997
|
+
When you need to build a carousel or slider, **always consult the carousel implementation guide first** (injected by `embeddables init` into your AI rules directory).
|
|
998
|
+
|
|
999
|
+
Key points:
|
|
1000
|
+
|
|
1001
|
+
- Swiper v11.2.10 is loaded at runtime via CDN — no build tooling required.
|
|
1002
|
+
- Turn any `Container` with child elements into a carousel via an action file that loads and initializes Swiper.
|
|
1003
|
+
- The guide covers the full pattern: page TSX structure, the initialization action, CSS, and optional prev/next navigation buttons.
|
|
1004
|
+
|
|
936
1005
|
### Adding a New Page
|
|
937
1006
|
|
|
938
1007
|
1. Create `pages/{pageKey}.page.tsx` with `'use client'` at the top and a default export.
|
|
@@ -959,14 +1028,14 @@ The `_docs` property (string only) documents non-obvious aspects of the embeddab
|
|
|
959
1028
|
### Adding a Computed Field
|
|
960
1029
|
|
|
961
1030
|
1. Create JS file: `computed-fields/{key}.js`
|
|
962
|
-
2. Implement `result()` function
|
|
1031
|
+
2. Implement `result(userData, helperFunctions, triggerContext)` function — access input values via `userData`, use `helperFunctions.trackCustomEvent()` for analytics if needed
|
|
963
1032
|
3. Add metadata to config.json: `computedFields` array
|
|
964
1033
|
4. Specify `inputs` array for dependencies
|
|
965
1034
|
|
|
966
1035
|
### Adding an Action
|
|
967
1036
|
|
|
968
1037
|
1. Create JS file: `actions/{name}.js`
|
|
969
|
-
2. Implement `output(
|
|
1038
|
+
2. Implement `output(userData, helperFunctions, triggerContext)` function (for custom actions) — access data via `userData`, use `triggerContext` for trigger metadata and `helperFunctions.trackCustomEvent()` for analytics
|
|
970
1039
|
3. Add metadata to config.json: `dataOutputs` array
|
|
971
1040
|
4. Configure action type and mappings in config.json
|
|
972
1041
|
5. Wire the action to a trigger: add the action's `id` to the appropriate `outputs_on*` field on the flow (config.json top-level), page (config.json pages entry), or component (React prop)
|
|
@@ -1050,9 +1119,14 @@ Add a `languages` prop to any component in your `.page.tsx` or `.location.tsx` f
|
|
|
1050
1119
|
/>
|
|
1051
1120
|
```
|
|
1052
1121
|
|
|
1053
|
-
### Adding Translations to OptionSelector
|
|
1122
|
+
### Adding Translations to OptionSelector
|
|
1123
|
+
|
|
1124
|
+
For OptionSelector components, translations can be added both:
|
|
1125
|
+
|
|
1126
|
+
- on the component itself (e.g., `label`, `placeholder`), and
|
|
1127
|
+
- on each **button** object (e.g., `text`, `description`).
|
|
1054
1128
|
|
|
1055
|
-
|
|
1129
|
+
Add a `languages` property at the appropriate level for the attributes being translated. The `OptionSelectorButtonWithLanguages` type (not plain `OptionSelectorButton`) is required for button constants since it includes the `languages` property:
|
|
1056
1130
|
|
|
1057
1131
|
```tsx
|
|
1058
1132
|
import { OptionSelector } from '@embeddables/cli/components'
|
|
@@ -1095,7 +1169,7 @@ Any text-based attribute on a component can be translated. Common translatable a
|
|
|
1095
1169
|
| `RichTextMarkdown` | `text` |
|
|
1096
1170
|
| `CustomButton` | `text`, `description` |
|
|
1097
1171
|
| `InputBox` | `label`, `placeholder`, `empty_invalid_message` |
|
|
1098
|
-
| `OptionSelector` | `label` (on the component); `text`, `description` (on each button) |
|
|
1172
|
+
| `OptionSelector` | `label`, `placeholder` (on the component); `text`, `description` (on each button) |
|
|
1099
1173
|
| `MediaImage` | `caption`, `alt_text` |
|
|
1100
1174
|
| `FileUpload` | `label` |
|
|
1101
1175
|
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Embeddables CLI
|
|
2
|
+
|
|
3
|
+
**Before editing embeddables, read [embeddables/AI-README.md](embeddables/AI-README.md) for the full guide.**
|
|
4
|
+
|
|
5
|
+
[embeddables/AI-README.md](embeddables/AI-README.md) is the single source of truth for working with Embeddables in this project. It covers:
|
|
6
|
+
|
|
7
|
+
- **Overview** — how the CLI transforms Embeddable JSON into local React/CSS/JS files
|
|
8
|
+
- **Embeddable Structure** — Flow, pages, components, styles, computed fields, actions
|
|
9
|
+
- **Component Types** — all types, allowed props, and the registry
|
|
10
|
+
- **File Layout** — pages/, global-components/, styles/, computed-fields/, actions/, config.json
|
|
11
|
+
- **CLI Commands** — pull, dev, build, save, inspect, init, branch
|
|
12
|
+
- **Compiler Pipeline** — forward (React → JSON) and reverse (JSON → React)
|
|
13
|
+
- **AI & Agent Rules** — command safety, types as source of truth, no publish commands
|
|
14
|
+
|
|
15
|
+
Re-inject this guide at any time by running `embeddables init` in your project root.
|
package/README.md
CHANGED
|
@@ -88,6 +88,48 @@ The project ID is stored in `embeddables.json` and enables interactive embeddabl
|
|
|
88
88
|
|
|
89
89
|
Authenticate with your Embeddables account.
|
|
90
90
|
|
|
91
|
+
**Interactive mode** (default):
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
embeddables login
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Prompts for email, sends OTP, then prompts for the code.
|
|
98
|
+
|
|
99
|
+
**Non-interactive mode** (for CI, automated environments, AI agents):
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Option 1: Token-based login (single step)
|
|
103
|
+
embeddables login --token <your-token>
|
|
104
|
+
|
|
105
|
+
# Option 2: OTP-based login (two steps)
|
|
106
|
+
embeddables login --email <your-email>
|
|
107
|
+
embeddables provide-otp --code <code-from-email>
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Options:
|
|
111
|
+
|
|
112
|
+
- `-t, --token <token>`: Use a token for non-interactive login (e.g. CI, cloud containers)
|
|
113
|
+
- `-e, --email <email>`: Send OTP to email address (non-interactive, step 1 of 2)
|
|
114
|
+
|
|
115
|
+
### `embeddables provide-otp`
|
|
116
|
+
|
|
117
|
+
Complete non-interactive login by providing the OTP code received via email. This is step 2 of the non-interactive OTP login flow.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# First, request OTP
|
|
121
|
+
embeddables login --email your@email.com
|
|
122
|
+
|
|
123
|
+
# Then, provide the code from your email
|
|
124
|
+
embeddables provide-otp --code 123456
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Options:
|
|
128
|
+
|
|
129
|
+
- `-c, --code <code>`: OTP code from email (required, 6-digit code)
|
|
130
|
+
|
|
131
|
+
The OTP code expires after 10 minutes. If expired, run `embeddables login --email` again to request a new code.
|
|
132
|
+
|
|
91
133
|
### `embeddables logout`
|
|
92
134
|
|
|
93
135
|
Clear stored authentication.
|
|
@@ -154,6 +196,58 @@ Options:
|
|
|
154
196
|
- `-s, --skip-build`: Skip the build step and use existing compiled JSON from `.generated/embeddable.json`
|
|
155
197
|
- `--from-version <number>`: Base version number (auto-detected from local config/files if not provided)
|
|
156
198
|
|
|
199
|
+
### `embeddables dangerously-publish`
|
|
200
|
+
|
|
201
|
+
Promote an **existing** saved version on the server to staging or production (same idea as the Builder: you pick a version, you don’t create a new one by default). **Use with caution** — this bypasses the normal review workflow. Requires login; production requires publisher/admin.
|
|
202
|
+
|
|
203
|
+
You must specify exactly one of `--staging` or `--prod`. If you omit both, the command exits with an error.
|
|
204
|
+
|
|
205
|
+
**Default (no `--save`)** — no new version is created:
|
|
206
|
+
|
|
207
|
+
- The version to promote is resolved in this order: `--publish-version`, then `_version` in `embeddables/<id>/config.json`, then the highest version inferred from `.generated/` snapshot filenames.
|
|
208
|
+
- The CLI calls the set-version-status API (main-line only; branch checkouts are rejected). The server still enforces rules (e.g. production only if that version is already on staging).
|
|
209
|
+
|
|
210
|
+
**With `--save`** — build, upload a **new** saved version, then publish **that** new version:
|
|
211
|
+
|
|
212
|
+
- Same build/upload flow as `embeddables save`; then promote to `--staging`, or to production with `--prod --via-staging` (**`--save --prod` without `--via-staging` is not allowed** — new versions must hit staging before production). The base version for the save is taken from `config.json` `_version` or `.generated/` snapshots (same as `embeddables save` without `--from-version`).
|
|
213
|
+
- Use `--skip-build` to reuse `.generated/embeddable.json`.
|
|
214
|
+
- Local snapshots after save are written as `embeddable-main@<version>.json` under `.generated/`.
|
|
215
|
+
|
|
216
|
+
Options:
|
|
217
|
+
|
|
218
|
+
- `-i, --id <id>`: Embeddable ID (will prompt from local embeddables if not provided)
|
|
219
|
+
- `-l, --label <label>`: Label for the new version (**only with `--save`**)
|
|
220
|
+
- `--skip-build`: Skip the build step (**only with `--save`**)
|
|
221
|
+
- `--publish-version <number>`: Promote this version number instead of `config.json` `_version` (no new version unless `--save`)
|
|
222
|
+
- `-p, --project-id <id>`: Project ID (will prompt if not configured)
|
|
223
|
+
- `--save`: Build (unless skipped), create a new saved version on the server, then publish it
|
|
224
|
+
- `--force`: Skip confirmation prompts on version conflicts (**when using `--save`**)
|
|
225
|
+
- `--staging`: Publish to staging
|
|
226
|
+
- `--prod`: Publish to production only (single API step; the version must **already** be on staging server-side)
|
|
227
|
+
- `--via-staging`: With `--prod`, push to staging first, then production (for when the version is not on staging yet)
|
|
228
|
+
|
|
229
|
+
Examples:
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
# Promote the version in config.json (_version) to staging (no new save)
|
|
233
|
+
embeddables dangerously-publish --staging
|
|
234
|
+
|
|
235
|
+
# Promote a specific existing version to staging
|
|
236
|
+
embeddables dangerously-publish --staging --publish-version 42
|
|
237
|
+
|
|
238
|
+
# Promote to production (version must already be staging on the server)
|
|
239
|
+
embeddables dangerously-publish --prod
|
|
240
|
+
|
|
241
|
+
# Production: staging first, then prod (e.g. version still SAVED)
|
|
242
|
+
embeddables dangerously-publish --prod --via-staging
|
|
243
|
+
|
|
244
|
+
# Build, save a new version, then publish that version to staging
|
|
245
|
+
embeddables dangerously-publish --save --staging
|
|
246
|
+
|
|
247
|
+
# Save, then staging and production (new version is not on staging yet)
|
|
248
|
+
embeddables dangerously-publish --save --prod --via-staging
|
|
249
|
+
```
|
|
250
|
+
|
|
157
251
|
### `embeddables dev`
|
|
158
252
|
|
|
159
253
|
Start a dev server with hot reload. Proxies to the Engine; by default uses production (`https://engine.embeddables.com`). Use `--local` to point to a local engine.
|