Skip to content

Commit c8e839c

Browse files
authored
[AI-8410] loadSidebarV2 host API bridge, context, and integration docs (#47)
* Implement ExternalHeadersCallback in host API bridge and update sidebar boot process - Added ExternalHeadersCallback type to support dynamic header retrieval. - Integrated initHostApiBridge function to initialize the host API with external headers. - Updated bootSidebar to call initHostApiBridge with the necessary configuration. - Created tests for host API bridge to validate header retrieval and error handling. - Ensured callbacks are preserved in the configuration resolution process. * Enhance host API bridge with website and analytics context retrieval - Added support for retrieving website and analytics context from the host configuration. - Updated initHostApiBridge to accept host configuration, including website and analytics data. - Implemented new message types for website and analytics context requests. - Enhanced tests to validate the retrieval of website and analytics context from the host. - Introduced localStorage handling for GET and SET operations in the host API bridge. * Demo * Docs and demos: loadSidebarV2 guide, full-config example, v1.4.9 Add integration README, full-config browser demo with host CSS, and link from root README.
1 parent 648584e commit c8e839c

9 files changed

Lines changed: 325 additions & 8 deletions

File tree

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ This SDK enables you to create custom MCP servers that Angie can discover and us
2727
- [Error Handling](#error-handling)
2828
- [Changelog](#changelog)
2929
- [Demo Plugin](#demo-plugin)
30+
- [Embedded Angie in any web page](#embedded-angie-loadsidebarv2)
3031
- [Debugging & Testing](#debugging--testing)
3132
- [FAQ](#faq)
3233

@@ -486,6 +487,11 @@ server.setRequestHandler(CallToolRequestSchema, async (req) => {
486487

487488
**For more examples, see the demo plugin and MCP server in the example folder**
488489

490+
## Embedded Angie in any web page
491+
492+
To embed the Angie UI in your own site (sidebar or floating chat), use `AngieMcpSdk.loadSidebarV2()`. See the dedicated guide: [loadSidebarV2](./src/load-sidebar-v2/README.md).
493+
494+
489495
If you have questions or need help, open an issue or contact the Elementor team!
490496

491497
## Debugging & Testing

demo/index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
<body>
1313
<h1>Angie SDK demos</h1>
1414
<a href="./load-sidebar-v1-demo/">loadSidebar (v1)</a>
15-
<a href="./load-sidebar-v2-floating-chat/">loadSidebarV2 — floatingChat</a>
1615
<a href="./load-sidebar-v2-sidebar/">loadSidebarV2 — sidebar</a>
16+
<a href="./load-sidebar-v2-floating-chat/">loadSidebarV2 — floatingChat</a>
17+
<a href="./load-sidebar-v2-full-config/">loadSidebarV2 — full example</a>
1718
</body>
1819
</html>

demo/load-sidebar-v2-floating-chat/index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
</style>
1818
</head>
1919
<body>
20-
<p>Host page — <code>loadSidebarV2</code> with <code>layout: LAYOUT_FLOATING_CHAT</code></p>
20+
<p><a href="../">← All demos</a></p>
21+
<p>Minimal <code>loadSidebarV2</code> with <code>layout: floatingChat</code>.</p>
2122
<div id="angie-sidebar-container" aria-hidden="true"></div>
2223
<script type="module" src="./host.js"></script>
2324
</body>
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Host-owned styles for loadSidebarV2 (sidebar layout).
3+
*
4+
* Toggle: your element — use the same selector as container.chatToggleButton.selector (#demo-toggle).
5+
* Panel: container.id (default angie-sidebar-container). SDK sets layout when open (body.angie-sidebar-active).
6+
* Iframe: #angie-iframe inside the container.
7+
*/
8+
9+
:root {
10+
--angie-sidebar-width: 380px;
11+
--angie-sidebar-z-index: 1300;
12+
/* Extra space between the sidebar panel and your page content */
13+
--angie-sidebar-content-gap: 1.5rem;
14+
}
15+
16+
@media (min-width: 768px) {
17+
body.angie-sidebar-active {
18+
padding-inline-start: calc(var(--angie-sidebar-width) + var(--angie-sidebar-content-gap)) !important;
19+
}
20+
}
21+
22+
/* Host toggle button (not injected by the SDK) */
23+
#demo-toggle {
24+
padding: 0.6rem 1.25rem;
25+
font: inherit;
26+
font-weight: 600;
27+
color: #fff;
28+
background: linear-gradient(135deg, #4f46e5, #7c3aed);
29+
border: none;
30+
border-radius: 999px;
31+
cursor: pointer;
32+
box-shadow: 0 4px 14px rgba(79, 70, 229, 0.35);
33+
}
34+
35+
#demo-toggle:hover {
36+
filter: brightness(1.05);
37+
}
38+
39+
#demo-toggle[aria-expanded="true"] {
40+
background: linear-gradient(135deg, #374151, #1f2937);
41+
}
42+
43+
/* Sidebar panel — override SDK defaults (see src/sidebar.css) */
44+
#angie-sidebar-container {
45+
background: #f8fafc;
46+
box-shadow: 4px 0 24px rgba(15, 23, 42, 0.12);
47+
border-inline-end: 1px solid #e2e8f0;
48+
}
49+
50+
body.angie-sidebar-active #angie-sidebar-container {
51+
border-inline-end-color: #c7d2fe;
52+
}
53+
54+
/* Angie iframe inside the panel */
55+
#angie-sidebar-container iframe#angie-iframe {
56+
border-radius: 12px 0 0 0;
57+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { AngieMcpSdk, LAYOUT_SIDEBAR } from '../../dist/index.js';
2+
3+
const root = document.getElementById( 'demo-host-app' );
4+
5+
const aiContext = {
6+
whatUserSees: {
7+
screen: 'Product editor',
8+
productName: root?.querySelector( '[data-field="name"]' )?.textContent?.trim() ?? 'Wireless Headphones',
9+
status: root?.dataset.status ?? 'draft',
10+
price: `$${ root?.dataset.price ?? '79.99' }`,
11+
sku: root?.dataset.sku ?? 'WH-100',
12+
description: root?.querySelector( '[data-field="description"]' )?.textContent?.trim() ?? '',
13+
},
14+
whatUserCanDo: [
15+
'Edit the product name, description, price, and status',
16+
'Publish the product when ready (currently draft)',
17+
'Ask Angie to improve the description or suggest a publish checklist',
18+
],
19+
};
20+
21+
const sdk = new AngieMcpSdk();
22+
23+
await sdk.loadSidebarV2( {
24+
host: {
25+
appId: 'demo-full-config',
26+
aiContext,
27+
},
28+
boot: {
29+
allowInIframe: false,
30+
},
31+
container: {
32+
// Default id angie-sidebar-container — matches SDK sidebar.css; override in demo-host.css
33+
layout: LAYOUT_SIDEBAR,
34+
styleTheme: 'wordpress',
35+
persistOpenState: true,
36+
resizable: true,
37+
chatToggleButton: {
38+
enabled: true,
39+
selector: '#demo-toggle',
40+
},
41+
},
42+
iframe: {
43+
origin: 'https://angie.elementor.com',
44+
path: 'angie/embedded',
45+
uiTheme: 'light',
46+
isRTL: false,
47+
},
48+
callbacks: {
49+
onClose: () => console.log( 'onClose' ),
50+
getExternalHeaders: async () => ( {
51+
'X-Demo-App-Id': 'demo-full-config',
52+
} ),
53+
},
54+
widgetConfig: {
55+
title: 'Angie',
56+
subtitle: 'Acme Store Builder',
57+
suggestions: {
58+
items: [
59+
{
60+
label: 'What am I looking at?',
61+
value: 'What do I see on this screen?',
62+
},
63+
{
64+
label: 'What can I do here?',
65+
value: 'What can I do on this screen?',
66+
},
67+
],
68+
},
69+
aiContextGuidance: { enabled: true },
70+
closeButton: 'collapse',
71+
},
72+
} );
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<title>loadSidebarV2 — full config example</title>
7+
<script type="importmap">
8+
{
9+
"imports": {
10+
"@modelcontextprotocol/sdk/types.js": "/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js",
11+
"zod/v4": "/node_modules/zod/v4/index.js"
12+
}
13+
}
14+
</script>
15+
<link rel="stylesheet" href="./demo-host.css" />
16+
<style>
17+
body { margin: 0; padding: 1rem; font-family: system-ui, sans-serif; max-width: 40rem; }
18+
a { color: #2563eb; }
19+
#demo-host-app { margin: 1rem 0; padding: 1rem; border: 1px solid #d8dde6; border-radius: 6px; }
20+
#demo-host-app h2 { margin: 0 0 0.5rem; font-size: 1.1rem; }
21+
</style>
22+
</head>
23+
<body>
24+
<p><a href="../">← All demos</a></p>
25+
<h1>loadSidebarV2 — full config</h1>
26+
<p>
27+
Options in <code>host.js</code>. Host styles in <code>demo-host.css</code>
28+
(<code>#demo-toggle</code>, <code>#angie-sidebar-container</code>, <code>iframe#angie-iframe</code>).
29+
</p>
30+
<main
31+
id="demo-host-app"
32+
data-screen-id="product-editor"
33+
data-product-id="prod_42"
34+
data-status="draft"
35+
data-price="79.99"
36+
data-sku="WH-100"
37+
>
38+
<h2 data-field="name">Wireless Headphones</h2>
39+
<p><strong>Status:</strong> draft · <strong>Price:</strong> $79.99 · <strong>SKU:</strong> WH-100</p>
40+
<p data-field="description">
41+
Lightweight over-ear headphones with noise isolation.
42+
</p>
43+
</main>
44+
<button type="button" id="demo-toggle">Open Angie</button>
45+
<div id="angie-sidebar-container" aria-hidden="true"></div>
46+
<script type="module" src="./host.js"></script>
47+
</body>
48+
</html>

demo/load-sidebar-v2-sidebar/index.html

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@
1414
</script>
1515
<style>
1616
body { margin: 0; padding: 1rem; font-family: system-ui, sans-serif; }
17-
#demo-sidebar-toggle {
18-
padding: 0.5rem 1rem;
19-
cursor: pointer;
20-
}
17+
#demo-sidebar-toggle { padding: 0.5rem 1rem; cursor: pointer; }
2118
</style>
2219
</head>
2320
<body>
24-
<p>Host page — <code>loadSidebarV2</code> with <code>layout: sidebar</code></p>
21+
<p><a href="../">← All demos</a></p>
22+
<p>Minimal <code>loadSidebarV2</code> with <code>layout: sidebar</code>.</p>
2523
<button type="button" id="demo-sidebar-toggle">Open Angie</button>
2624
<div id="angie-sidebar-container" aria-hidden="true"></div>
2725
<script type="module" src="./host.js"></script>

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@elementor/angie-sdk",
3-
"version": "1.4.8",
3+
"version": "1.4.9",
44
"description": "TypeScript SDK for Angie AI assistant",
55
"main": "dist/index.cjs",
66
"module": "dist/index.js",

src/load-sidebar-v2/README.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
# loadSidebarV2
2+
3+
Embeds the Angie assistant in a host page (SaaS app, WordPress frontend, or any site) via an iframe and a small host-side shell. Use `AngieMcpSdk.loadSidebarV2()` from the public package API.
4+
5+
## Quick start
6+
7+
```typescript
8+
import { AngieMcpSdk, LAYOUT_FLOATING_CHAT } from '@elementor/angie-sdk';
9+
10+
const sdk = new AngieMcpSdk();
11+
12+
await sdk.loadSidebarV2({
13+
host: { appId: 'my-app' },
14+
container: { layout: LAYOUT_FLOATING_CHAT },
15+
});
16+
```
17+
18+
**Sidebar layout** (dock-style panel, resizable, open state persisted):
19+
20+
```typescript
21+
await sdk.loadSidebarV2({
22+
host: { appId: 'my-app' },
23+
container: {
24+
layout: 'sidebar',
25+
styleTheme: 'wordpress',
26+
chatToggleButton: {
27+
enabled: true,
28+
selector: '#my-toggle',
29+
},
30+
},
31+
});
32+
```
33+
34+
Local demos:
35+
36+
- [`demo/load-sidebar-v2-sidebar/`](../../demo/load-sidebar-v2-sidebar/) — minimal sidebar
37+
- [`demo/load-sidebar-v2-floating-chat/`](../../demo/load-sidebar-v2-floating-chat/) — minimal floating chat
38+
- [`demo/load-sidebar-v2-full-config/`](../../demo/load-sidebar-v2-full-config/) — full example (`aiContext`, custom CSS)
39+
40+
## Layouts
41+
42+
| Layout | Constant | Typical use |
43+
|--------|----------|-------------|
44+
| Sidebar | `LAYOUT_SIDEBAR` (`'sidebar'`) | Fixed side panel, resize, persist open/closed |
45+
| Floating chat | `LAYOUT_FLOATING_CHAT` (`'floatingChat'`) | Bottom-corner widget with optional injected toggle button |
46+
47+
Each layout applies [presets](./presets/) (defaults for `persistOpenState`, `resizable`, `chatToggleButton`, etc.). Override any field via `container` options.
48+
49+
## Configuration
50+
51+
`LoadSidebarV2Options` (see [`config.ts`](./config.ts)):
52+
53+
| Section | Purpose |
54+
|---------|---------|
55+
| `host` | **Required.** `appId`, optional `aiContext`, `website`, `analytics` sent to the embedded Angie app (see [aiContext](#hostaicontext)) |
56+
| `boot` | `allowInIframe` — skip boot when the host page is itself in an iframe (default `false`) |
57+
| `container` | DOM container id, `layout`, `styleTheme` (`'wordpress'` injects WP admin-bar CSS), resize/persist flags, chat toggle button |
58+
| `iframe` | Angie origin, path (`angie/embedded`), `uiTheme`, `isRTL` |
59+
| `callbacks` | `onClose`, `getExternalHeaders` for auth/API headers |
60+
| `widgetConfig` | Close button behavior (`collapse` vs `close`); layout-specific defaults in [`widget-config.ts`](./widget-config.ts) |
61+
62+
Embedded config uses `configVersion: 2` (`LOAD_SIDEBAR_V2_CONFIG_VERSION`).
63+
64+
### host.aiContext
65+
66+
Object passed in `embedded.aiContext` on `HOST_READY` (and `sdk-embedded-config`). The embedded Angie app injects it into the agent so replies can use your host app state.
67+
68+
Keep it focused on what helps the agent answer screen-level questions:
69+
70+
| Key | Purpose |
71+
|-----|---------|
72+
| `whatUserSees` | What is on the current screen (labels, selection, visible fields) |
73+
| `whatUserCanDo` | Actions the user can take on this screen |
74+
75+
Example: [`demo/load-sidebar-v2-full-config/host.js`](../../demo/load-sidebar-v2-full-config/host.js) reads `#demo-host-app` into `whatUserSees` and lists allowed actions in `whatUserCanDo`.
76+
77+
Enable `widgetConfig.aiContextGuidance: { enabled: true }` so users see that host context is available.
78+
79+
### Custom CSS (toggle + sidebar panel)
80+
81+
| Target | Selector | Notes |
82+
|--------|----------|--------|
83+
| Toggle button | Your selector (e.g. `#my-angie-toggle`) | Host DOM; wire via `container.chatToggleButton.selector` |
84+
| Sidebar panel | `#${container.id}` (default `#angie-sidebar-container`) | SDK injects layout rules in `src/sidebar.css` |
85+
| Panel width / z-index | `:root { --angie-sidebar-width; --angie-sidebar-z-index; }` | Read by SDK when opening/resizing |
86+
| Gap from sidebar | `body.angie-sidebar-active { padding-inline-start: calc(var(--angie-sidebar-width) + 1.5rem) }` | SDK sets padding to width only; add your own gap (see demo CSS) |
87+
| Iframe | `#angie-sidebar-container iframe#angie-iframe` | `id="angie-iframe"` is set by the SDK |
88+
89+
If you use a custom `container.id`, copy or adapt the rules from `sidebar.css` for your id.
90+
91+
Example: [`demo/load-sidebar-v2-full-config/demo-host.css`](../../demo/load-sidebar-v2-full-config/demo-host.css).
92+
93+
## Boot flow
94+
95+
```
96+
loadSidebarV2(options)
97+
→ resolveConfig + shouldBoot
98+
→ initHostApiBridge (postMessage API)
99+
→ ensureSidebarContainer
100+
→ layout strategy (initShell → open iframe → afterOpen)
101+
→ sendEmbeddedConfig / sendWidgetConfig
102+
```
103+
104+
Entry point: [`boot-sidebar.ts`](./boot-sidebar.ts). Layout strategies: [`layouts/index.ts`](./layouts/index.ts).
105+
106+
## Host API bridge
107+
108+
[`host-api-bridge.ts`](./host-api-bridge.ts) listens for messages from the Angie iframe (origin-checked) and responds on a `MessagePort`:
109+
110+
- `GET_EXTERNAL_HEADERS``callbacks.getExternalHeaders()`
111+
- `angie/context/get-website-context` — host + document metadata
112+
- `angie/context/get-analytics-context` — screen path + `host.analytics`
113+
- Host localStorage get/set (for embedded persistence)
114+
115+
## Module map
116+
117+
| File / folder | Role |
118+
|---------------|------|
119+
| `boot-sidebar.ts` | Orchestration |
120+
| `resolve-config.ts` | Merge options, env, presets |
121+
| `open-embedded-iframe.ts` | Open iframe via shared `iframe.ts` |
122+
| `embedded-handshake.ts` | Post-open config messages |
123+
| `shell.ts`, `sidebar-toggle.ts` | Sidebar layout DOM/state |
124+
| `chat-toggle/` | Floating chat shell and toggle UI |
125+
| `presets/` | Per-layout defaults |
126+
| `inject-style-theme.ts` | Optional WordPress theme CSS |
127+
128+
## Tests
129+
130+
Jest specs live next to modules (`*.test.ts`). Run the package test script from the repo root.
131+
132+
## Exports
133+
134+
From `@elementor/angie-sdk`: `loadSidebarV2` on `AngieMcpSdk`, plus `LAYOUT_SIDEBAR`, `LAYOUT_FLOATING_CHAT`, `LoadSidebarV2Options`, `ExternalHeadersCallback`.

0 commit comments

Comments
 (0)