@one-million-lines/email-builder 0.1.4 → 0.2.1

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/CHANGELOG.md CHANGED
@@ -6,6 +6,24 @@ This project adheres to [Semantic Versioning](https://semver.org/) and the
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ### Added
10
+ - **AI assistant module** (`src/ai/`) — optional chat-driven editing. When an AI
11
+ provider is configured an **AI** tab appears in the left sidebar. The client
12
+ sends a catalog of real modules so the assistant only ever assembles valid,
13
+ renderable emails; every response is validated with `documentSchema` and gets
14
+ fresh ids before being applied. New exports: `aiAssistantPlugin`,
15
+ `createHttpAIProvider`, `buildCatalog`, `applyAIResponse`, `mockAIProvider`,
16
+ `applyAIActions`, `validateAIDocument`, and the related types. New `aiEndpoint`
17
+ prop on `<EmailBuilder>`. See `src/ai/README.md`.
18
+ - **Python AI backend** (`backend/`) — a small Flask service (`app.py`,
19
+ `ai_service.py`) exposing `GET /health` and `POST /ai/generate`. Works with any
20
+ OpenAI-compatible endpoint and falls back to a deterministic offline planner
21
+ when no API key is set. See `backend/README.md`.
22
+ - **Feature module additions** — five new blocks added to the Feature category:
23
+ `feature.gradient_hero` (launch CTA hero), `feature.pull_quote`,
24
+ `feature.stat_row`, `feature.single_product_spotlight`, `feature.pill_nav`.
25
+
26
+
9
27
  ## [0.1.4] — 2026-07-02
10
28
 
11
29
  ### Changed
package/README.md CHANGED
@@ -15,6 +15,8 @@ Marketing teams often need a reusable editor that produces email-safe HTML witho
15
15
  - Visual editor with top bar, left sidebar, canvas, and right sidebar
16
16
  - JSON-first email document model
17
17
  - Built-in modules, templates, and themes
18
+ - **AI assistant module** — optional chat-driven editing backed by a simple
19
+ Python service
18
20
  - Table-based HTML rendering for email output
19
21
  - Local autosave via Zustand store
20
22
  - React component API and vanilla JS factory
@@ -45,12 +47,14 @@ src/
45
47
  core/ document types, renderer, validation, AI actions, plugins
46
48
  editor/ top bar, sidebars, canvas
47
49
  modules/ module registry and built-in modules
50
+ ai/ AI assistant module: provider, catalog, chat panel (see ai/README.md)
48
51
  recommendations/ recommendation and fallback logic
49
52
  store/ editor state and persistence
50
53
  templates/ built-in email templates
51
54
  themes/ theme definitions
52
55
  plugins/ extension points, including image uploader helpers
53
56
  index.ts public API for embedding
57
+ backend/ Python (Flask) AI service (see backend/README.md)
54
58
  ```
55
59
 
56
60
  ## Install
@@ -131,6 +135,26 @@ Vue and Angular mount the React-based editor through the framework-neutral
131
135
  `createEmailBuilder()` factory (`getDocument` / `exportHtml` / `exportJson` /
132
136
  `destroy`). React and ReactDOM remain peer dependencies in all cases.
133
137
 
138
+ ### AI assistant module
139
+
140
+ Enable chat-driven editing by pointing the builder at the Python AI service in
141
+ [`backend/`](./backend). When configured, an **AI** tab appears in the left
142
+ sidebar.
143
+
144
+ ```tsx
145
+ <EmailBuilder aiEndpoint="http://localhost:3001/ai/generate" />
146
+ ```
147
+
148
+ ```ts
149
+ import { registerPlugin, aiAssistantPlugin } from "@one-million-lines/email-builder";
150
+ registerPlugin(aiAssistantPlugin({ endpoint: "http://localhost:3001/ai/generate" }));
151
+ ```
152
+
153
+ The assistant only ever assembles emails from real, renderable modules, and every
154
+ response is validated with Zod before it is applied. See
155
+ [`src/ai/README.md`](./src/ai/README.md) and
156
+ [`backend/README.md`](./backend/README.md).
157
+
134
158
  ### Styling & isolation
135
159
 
136
160
  The stylesheet at `@one-million-lines/email-builder/styles.css` is designed to
@@ -212,6 +236,20 @@ npm run validate:pack # build + npm pack --dry-run
212
236
  npm run build:demo # build the standalone demo app
213
237
  ```
214
238
 
239
+ ### AI backend (optional)
240
+
241
+ The AI assistant needs the Python service in [`backend/`](./backend):
242
+
243
+ ```bash
244
+ cd backend
245
+ python -m venv .venv && source .venv/bin/activate
246
+ pip install -r requirements.txt
247
+ python app.py # offline heuristic mode; set AI_API_KEY for a real model
248
+ ```
249
+
250
+ Then set `VITE_AI_ENDPOINT=http://localhost:3001/ai/generate` in a root `.env`
251
+ to enable the AI tab in `npm run dev`.
252
+
215
253
  ## Building & publishing
216
254
 
217
255
  ```bash
@@ -0,0 +1,3 @@
1
+ /** Hook: is an AI provider currently configured? Reactive. */
2
+ export declare function useAIAvailable(): boolean;
3
+ export declare function AIChatPanel(): import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { EmailDocument } from '../core/types';
2
+ import { AIResponse } from '../core/aiActions';
3
+ export interface ApplyResult {
4
+ document: EmailDocument;
5
+ /** Assistant chat text, if any. */
6
+ text?: string;
7
+ /** Short human summary of what changed, for the chat log. */
8
+ summary: string;
9
+ }
10
+ /**
11
+ * Apply an {@link AIResponse} to `doc`. Returns the next document plus a summary,
12
+ * or an error string when the result fails validation.
13
+ */
14
+ export declare function applyAIResponse(doc: EmailDocument, res: AIResponse): {
15
+ ok: true;
16
+ result: ApplyResult;
17
+ } | {
18
+ ok: false;
19
+ error: string;
20
+ };
@@ -0,0 +1,15 @@
1
+ import { EmailModule } from '../core/types';
2
+ export interface CatalogEntry {
3
+ type: string;
4
+ category: string;
5
+ name: string;
6
+ description: string;
7
+ tags: string[];
8
+ source: "module";
9
+ /** A concrete, renderable instance of the module (ids included). */
10
+ sample: EmailModule;
11
+ }
12
+ /**
13
+ * Build the catalog sent to the AI backend.
14
+ */
15
+ export declare function buildCatalog(): CatalogEntry[];
@@ -0,0 +1,22 @@
1
+ import { Plugin } from '../core/plugins';
2
+ import { HttpAIProviderOptions } from './provider';
3
+ export { createHttpAIProvider } from './provider';
4
+ export type { HttpAIProviderOptions } from './provider';
5
+ export { buildCatalog } from './catalog';
6
+ export type { CatalogEntry } from './catalog';
7
+ export { applyAIResponse } from './applyResponse';
8
+ export type { ApplyResult } from './applyResponse';
9
+ export { AIChatPanel, useAIAvailable } from './AIChatPanel';
10
+ export { getAIProvider as getActiveAIProvider, setAIProvider as setActiveAIProvider, } from './state';
11
+ export interface AIAssistantOptions extends HttpAIProviderOptions {
12
+ }
13
+ /**
14
+ * Plugin factory that wires an HTTP-backed AI provider into the builder and
15
+ * enables the chat panel.
16
+ *
17
+ * @example
18
+ * import { registerPlugin } from "@one-million-lines/email-builder";
19
+ * import { aiAssistantPlugin } from "@one-million-lines/email-builder";
20
+ * registerPlugin(aiAssistantPlugin({ endpoint: "http://localhost:3001/ai/generate" }));
21
+ */
22
+ export declare function aiAssistantPlugin(opts: AIAssistantOptions): Plugin;
@@ -0,0 +1,23 @@
1
+ import { AIProvider, AIResponse } from '../core/aiActions';
2
+ export interface HttpAIProviderOptions {
3
+ /** Absolute or same-origin URL that implements POST /ai/generate. Required. */
4
+ endpoint: string;
5
+ /** Extra headers (e.g. Authorization). */
6
+ headers?: Record<string, string>;
7
+ /** Send cookies with the request. Default: false. */
8
+ withCredentials?: boolean;
9
+ /** Abort the request after this many ms. Default: 60000. */
10
+ timeoutMs?: number;
11
+ /**
12
+ * Map a raw server JSON body to an `AIResponse`.
13
+ * Default expects the body to already be an `AIResponse`.
14
+ */
15
+ transformResponse?: (body: unknown) => AIResponse;
16
+ }
17
+ /**
18
+ * Create an {@link AIProvider} backed by an HTTP endpoint.
19
+ *
20
+ * @example
21
+ * const provider = createHttpAIProvider({ endpoint: "http://localhost:3001/ai/generate" });
22
+ */
23
+ export declare function createHttpAIProvider(opts: HttpAIProviderOptions): AIProvider;
@@ -0,0 +1,11 @@
1
+ import { AIProvider } from '../core/aiActions';
2
+ type Listener = () => void;
3
+ /** Set (or clear) the active AI provider. Notifies subscribers. */
4
+ export declare function setAIProvider(next: AIProvider | null): void;
5
+ /** The active AI provider, or null when AI is not configured. */
6
+ export declare function getAIProvider(): AIProvider | null;
7
+ /** Subscribe to provider changes (for `useSyncExternalStore`). */
8
+ export declare const subscribeAIProvider: (listener: Listener) => (() => void);
9
+ /** Monotonic version; changes whenever the provider is set or cleared. */
10
+ export declare const getAIProviderVersion: () => number;
11
+ export {};