@delmaredigital/payload-puck 0.6.14 → 0.6.15

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/README.md CHANGED
@@ -2,9 +2,21 @@
2
2
 
3
3
  A PayloadCMS plugin for integrating [Puck](https://puckeditor.com) visual page builder. Build pages visually with drag-and-drop components while leveraging Payload's content management capabilities.
4
4
 
5
- [![Demo](https://img.shields.io/badge/Demo-Live-blue)](https://demo.delmaredigital.com)
6
- [![Starter Template](https://img.shields.io/badge/Starter-Template-green)](https://github.com/delmaredigital/dd-starter)
7
- [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdelmaredigital%2Fdd-starter&project-name=my-payload-site&build-command=pnpm%20run%20ci&env=PAYLOAD_SECRET,BETTER_AUTH_SECRET&stores=%5B%7B%22type%22%3A%22integration%22%2C%22protocol%22%3A%22storage%22%2C%22productSlug%22%3A%22neon%22%2C%22integrationSlug%22%3A%22neon%22%7D%2C%7B%22type%22%3A%22blob%22%7D%5D)
5
+ <p align="center">
6
+ <a href="https://demo.delmaredigital.com"><img src="https://img.shields.io/badge/Live_Demo-Try_It_Now-2ea44f?style=for-the-badge&logo=vercel&logoColor=white" alt="Live Demo - Try It Now"></a>
7
+ &nbsp;&nbsp;
8
+ <a href="https://github.com/delmaredigital/dd-starter"><img src="https://img.shields.io/badge/Starter_Template-Use_This-blue?style=for-the-badge&logo=github&logoColor=white" alt="Starter Template - Use This"></a>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fdelmaredigital%2Fdd-starter&project-name=my-payload-site&build-command=pnpm%20run%20ci&env=PAYLOAD_SECRET,BETTER_AUTH_SECRET&stores=%5B%7B%22type%22%3A%22integration%22%2C%22protocol%22%3A%22storage%22%2C%22productSlug%22%3A%22neon%22%2C%22integrationSlug%22%3A%22neon%22%7D%2C%7B%22type%22%3A%22blob%22%7D%5D"><img src="https://vercel.com/button" alt="Deploy with Vercel" height="32"></a>
13
+ </p>
14
+
15
+ ---
16
+
17
+ ## Documentation
18
+
19
+ For additional documentation, visit: [https://deepwiki.com/delmaredigital/payload-puck](https://deepwiki.com/delmaredigital/payload-puck)
8
20
 
9
21
  ---
10
22
 
@@ -75,8 +75,10 @@
75
75
  puckTools[name] = tool({
76
76
  description: toolDef.description,
77
77
  inputSchema: toolDef.inputSchema,
78
+ outputSchema: toolDef.outputSchema,
78
79
  execute: toolDef.execute,
79
- name: toolDef.name
80
+ name: toolDef.name,
81
+ mode: toolDef.mode
80
82
  });
81
83
  }
82
84
  }
@@ -86,7 +88,8 @@
86
88
  host: ai?.host,
87
89
  ai: {
88
90
  context: ai?.context,
89
- tools: Object.keys(puckTools).length > 0 ? puckTools : undefined
91
+ tools: Object.keys(puckTools).length > 0 ? puckTools : undefined,
92
+ onFinish: ai?.onFinish
90
93
  }
91
94
  });
92
95
  } catch (error) {
@@ -80,8 +80,10 @@
80
80
  puckTools[name] = tool({
81
81
  description: toolDef.description,
82
82
  inputSchema: toolDef.inputSchema,
83
+ outputSchema: toolDef.outputSchema,
83
84
  execute: toolDef.execute,
84
- name: toolDef.name
85
+ name: toolDef.name,
86
+ mode: toolDef.mode
85
87
  });
86
88
  }
87
89
  }
@@ -93,7 +95,8 @@
93
95
  apiKey: ai?.apiKey,
94
96
  host: ai?.host,
95
97
  pageData,
96
- tools: Object.keys(puckTools).length > 0 ? puckTools : undefined
98
+ tools: Object.keys(puckTools).length > 0 ? puckTools : undefined,
99
+ onFinish: ai?.onFinish
97
100
  });
98
101
  if (!result) {
99
102
  throw new Error('AI generation failed: no result returned');
@@ -34,7 +34,7 @@
34
34
  * <PuckEditor plugins={[aiPlugin]} />
35
35
  * ```
36
36
  */ export function createAiPlugin(options = {}) {
37
- const { host = '/api/puck', examplePrompts, onSubmit } = options;
37
+ const { host = '/api/puck', examplePrompts, onSubmit, prepareRequest, scrollTracking } = options;
38
38
  // Dynamic import to avoid build errors if plugin-ai not installed
39
39
  // Also allows for tree-shaking when AI is not used
40
40
  try {
@@ -45,7 +45,9 @@
45
45
  chat: {
46
46
  examplePrompts,
47
47
  onSubmit
48
- }
48
+ },
49
+ prepareRequest,
50
+ scrollTracking
49
51
  });
50
52
  } catch (e) {
51
53
  // Return a placeholder plugin if @puckeditor/plugin-ai is not installed
@@ -58,6 +58,18 @@ export interface AiTool<TInput = unknown, TOutput = unknown> {
58
58
  * Optional name for the tool
59
59
  */
60
60
  name?: string;
61
+ /**
62
+ * Controls how the tool interacts with the AI agent.
63
+ * - "auto": Agent decides when to use the tool (default)
64
+ * - "preload": Tool result is fetched before generation starts
65
+ * - "inline": Tool runs inline during generation
66
+ */
67
+ mode?: 'auto' | 'preload' | 'inline';
68
+ /**
69
+ * Optional output schema for the tool's return value.
70
+ * Must be a Zod schema.
71
+ */
72
+ outputSchema?: import('zod').ZodTypeAny;
61
73
  }
62
74
  /**
63
75
  * AI-specific configuration options for the server-side handler
@@ -97,6 +109,27 @@ export interface AiOptions {
97
109
  * Custom Puck Cloud host URL
98
110
  */
99
111
  host?: string;
112
+ /**
113
+ * Callback invoked when AI generation finishes.
114
+ * Provides usage metrics for cost tracking and analytics.
115
+ *
116
+ * @example
117
+ * ```typescript
118
+ * onFinish: ({ totalCost, tokenUsage }) => {
119
+ * console.log(`Used ${tokenUsage.totalTokens} tokens, cost: ${totalCost}`)
120
+ * }
121
+ * ```
122
+ */
123
+ onFinish?: (result: {
124
+ totalCost: number;
125
+ tokenUsage: {
126
+ inputTokens: number | undefined;
127
+ outputTokens: number | undefined;
128
+ totalTokens: number | undefined;
129
+ reasoningTokens?: number | undefined;
130
+ cachedInputTokens?: number | undefined;
131
+ };
132
+ }) => void;
100
133
  }
101
134
  /**
102
135
  * AI configuration for a Puck field
@@ -140,6 +173,25 @@ export interface AiFieldConfig {
140
173
  type: string;
141
174
  [key: string]: unknown;
142
175
  };
176
+ /**
177
+ * Bind this field to a named tool. The tool runs during generation
178
+ * and its result populates this field deterministically.
179
+ *
180
+ * @example
181
+ * ```typescript
182
+ * fields: {
183
+ * src: {
184
+ * type: 'text',
185
+ * ai: { bind: 'getImageUrl' },
186
+ * },
187
+ * }
188
+ * ```
189
+ */
190
+ bind?: string;
191
+ /**
192
+ * Whether this field's value should be streamed during AI generation.
193
+ */
194
+ stream?: boolean;
143
195
  }
144
196
  /**
145
197
  * AI configuration for a Puck component
@@ -161,8 +213,21 @@ export interface AiComponentConfig {
161
213
  */
162
214
  instructions?: string;
163
215
  /**
164
- * Custom JSON schema for the entire component.
165
- * For when Puck can't infer from fields.
216
+ * Exclude this component from AI generation.
217
+ * The component remains available for manual use in the editor.
218
+ */
219
+ exclude?: boolean;
220
+ /**
221
+ * Controls which components are allowed/disallowed in the default slot zone
222
+ * during AI generation.
223
+ */
224
+ defaultZone?: {
225
+ allow?: string[];
226
+ disallow?: string[];
227
+ disabled?: boolean;
228
+ };
229
+ /**
230
+ * @deprecated Removed in Puck AI 0.4. Use field-level `schema` on `AiFieldConfig` instead.
166
231
  */
167
232
  schema?: {
168
233
  type: 'object';
@@ -319,6 +384,34 @@ export interface AiPluginOptions {
319
384
  * Callback when user submits a prompt
320
385
  */
321
386
  onSubmit?: (prompt: string) => void;
387
+ /**
388
+ * Intercept and modify outgoing AI requests before they are sent.
389
+ * Use this to add custom headers, credentials, or body data.
390
+ *
391
+ * @example
392
+ * ```typescript
393
+ * prepareRequest: (opts) => ({
394
+ * ...opts,
395
+ * headers: { ...opts.headers, Authorization: 'Bearer token' },
396
+ * credentials: 'include',
397
+ * })
398
+ * ```
399
+ */
400
+ prepareRequest?: (opts: {
401
+ body?: {
402
+ chatId?: string;
403
+ trigger?: string;
404
+ [key: string]: any;
405
+ };
406
+ headers?: HeadersInit;
407
+ credentials?: RequestCredentials;
408
+ }) => any | Promise<any>;
409
+ /**
410
+ * Enable automatic scroll tracking during AI generation.
411
+ * When true, the editor scrolls to follow new content as it streams in.
412
+ * @default true (in @puckeditor/plugin-ai 0.4+)
413
+ */
414
+ scrollTracking?: boolean;
322
415
  }
323
416
  /**
324
417
  * AI configuration overrides for components
@@ -503,4 +596,18 @@ export interface PuckPluginAiConfig {
503
596
  * ```
504
597
  */
505
598
  componentInstructions?: ComponentAiOverrides;
599
+ /**
600
+ * Callback invoked when AI generation finishes.
601
+ * Provides usage metrics for cost tracking and analytics.
602
+ */
603
+ onFinish?: (result: {
604
+ totalCost: number;
605
+ tokenUsage: {
606
+ inputTokens: number | undefined;
607
+ outputTokens: number | undefined;
608
+ totalTokens: number | undefined;
609
+ reasoningTokens?: number | undefined;
610
+ cachedInputTokens?: number | undefined;
611
+ };
612
+ }) => void;
506
613
  }
@@ -177,6 +177,24 @@ export interface PuckEditorProps {
177
177
  * Users can click these to quickly send common prompts.
178
178
  */
179
179
  examplePrompts?: AiExamplePrompt[];
180
+ /**
181
+ * Intercept and modify outgoing AI requests before they are sent.
182
+ * Use this to add custom headers, credentials, or body data.
183
+ */
184
+ prepareRequest?: (opts: {
185
+ body?: {
186
+ chatId?: string;
187
+ trigger?: string;
188
+ [key: string]: any;
189
+ };
190
+ headers?: HeadersInit;
191
+ credentials?: RequestCredentials;
192
+ }) => any | Promise<any>;
193
+ /**
194
+ * Enable automatic scroll tracking during AI generation.
195
+ * @default true
196
+ */
197
+ scrollTracking?: boolean;
180
198
  };
181
199
  /**
182
200
  * Example prompts from plugin config.
@@ -126,6 +126,16 @@ export interface PuckEditorImplProps {
126
126
  aiOptions?: {
127
127
  host?: string;
128
128
  examplePrompts?: AiExamplePrompt[];
129
+ prepareRequest?: (opts: {
130
+ body?: {
131
+ chatId?: string;
132
+ trigger?: string;
133
+ [key: string]: any;
134
+ };
135
+ headers?: HeadersInit;
136
+ credentials?: RequestCredentials;
137
+ }) => any | Promise<any>;
138
+ scrollTracking?: boolean;
129
139
  };
130
140
  /**
131
141
  * Whether the puck-ai-prompts collection is enabled.
@@ -532,7 +532,9 @@ import { useAiPrompts } from '../ai/hooks/useAiPrompts.js';
532
532
  host: aiOptions?.host || '/api/puck/ai',
533
533
  chat: {
534
534
  examplePrompts: convertedPrompts.length > 0 ? convertedPrompts : undefined
535
- }
535
+ },
536
+ prepareRequest: aiOptions?.prepareRequest,
537
+ scrollTracking: aiOptions?.scrollTracking
536
538
  });
537
539
  // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally omit prompt arrays to prevent recreation
538
540
  }, [
@@ -15,6 +15,19 @@ export interface AiEndpointOptions {
15
15
  * These allow AI to perform actions like database lookups, API calls, etc.
16
16
  */
17
17
  tools?: Record<string, AiTool>;
18
+ /**
19
+ * Callback invoked when AI generation finishes with usage metrics.
20
+ */
21
+ onFinish?: (result: {
22
+ totalCost: number;
23
+ tokenUsage: {
24
+ inputTokens: number | undefined;
25
+ outputTokens: number | undefined;
26
+ totalTokens: number | undefined;
27
+ reasoningTokens?: number | undefined;
28
+ cachedInputTokens?: number | undefined;
29
+ };
30
+ }) => void;
18
31
  }
19
32
  /**
20
33
  * Creates a Payload endpoint handler for Puck AI
@@ -201,6 +201,8 @@ import { pagePatternSystemContext } from '../ai/presets/index.js';
201
201
  name: tool.name,
202
202
  description: tool.description,
203
203
  inputSchema: tool.inputSchema,
204
+ outputSchema: tool.outputSchema,
205
+ mode: tool.mode,
204
206
  // Wrap execute to inject Payload context
205
207
  execute: (input)=>tool.execute(input, toolContext)
206
208
  };
@@ -208,7 +210,10 @@ import { pagePatternSystemContext } from '../ai/presets/index.js';
208
210
  aiOptions.tools = wrappedTools;
209
211
  }
210
212
  const response = await puckHandler(webRequest, {
211
- ai: aiOptions,
213
+ ai: {
214
+ ...aiOptions,
215
+ onFinish: options.onFinish
216
+ },
212
217
  apiKey
213
218
  });
214
219
  if (!response.ok) {
@@ -293,7 +293,8 @@ import { createContextListHandler, createContextCreateHandler, createContextUpda
293
293
  method: 'post',
294
294
  handler: createAiEndpointHandler({
295
295
  context: aiConfig.context,
296
- tools: aiConfig.tools
296
+ tools: aiConfig.tools,
297
+ onFinish: aiConfig.onFinish
297
298
  })
298
299
  }
299
300
  ] : [],
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "0.6.14";
1
+ export declare const VERSION = "0.6.15";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Auto-generated by scripts/generate-version.js - do not edit manually
2
- export const VERSION = '0.6.14';
2
+ export const VERSION = '0.6.15';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@delmaredigital/payload-puck",
3
- "version": "0.6.14",
3
+ "version": "0.6.15",
4
4
  "description": "Puck visual page builder plugin for Payload CMS",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -141,36 +141,36 @@
141
141
  }
142
142
  },
143
143
  "dependencies": {
144
- "@puckeditor/cloud-client": "^0.5.0",
145
- "@puckeditor/plugin-ai": "^0.5.0",
144
+ "@puckeditor/cloud-client": "^0.6.0",
145
+ "@puckeditor/plugin-ai": "^0.6.0",
146
146
  "@puckeditor/plugin-heading-analyzer": "^0.21.1",
147
147
  "@radix-ui/react-popover": "^1.1.15",
148
- "@tiptap/core": "^3.18.0",
149
- "@tiptap/extension-color": "^3.18.0",
150
- "@tiptap/extension-highlight": "^3.18.0",
151
- "@tiptap/extension-link": "^3.18.0",
152
- "@tiptap/extension-subscript": "^3.18.0",
153
- "@tiptap/extension-superscript": "^3.18.0",
154
- "@tiptap/extension-text-align": "^3.18.0",
155
- "@tiptap/extension-text-style": "^3.18.0",
156
- "@tiptap/extension-underline": "^3.18.0",
157
- "@tiptap/react": "^3.18.0",
158
- "@tiptap/starter-kit": "^3.18.0",
159
- "html-react-parser": "^5.2.13",
148
+ "@tiptap/core": "^3.20.0",
149
+ "@tiptap/extension-color": "^3.20.0",
150
+ "@tiptap/extension-highlight": "^3.20.0",
151
+ "@tiptap/extension-link": "^3.20.0",
152
+ "@tiptap/extension-subscript": "^3.20.0",
153
+ "@tiptap/extension-superscript": "^3.20.0",
154
+ "@tiptap/extension-text-align": "^3.20.0",
155
+ "@tiptap/extension-text-style": "^3.20.0",
156
+ "@tiptap/extension-underline": "^3.20.0",
157
+ "@tiptap/react": "^3.20.0",
158
+ "@tiptap/starter-kit": "^3.20.0",
159
+ "html-react-parser": "^5.2.17",
160
160
  "lucide-react": "^0.469.0"
161
161
  },
162
162
  "devDependencies": {
163
- "@payloadcms/next": "^3.73.0",
164
- "@payloadcms/ui": "^3.73.0",
163
+ "@payloadcms/next": "^3.77.0",
164
+ "@payloadcms/ui": "^3.77.0",
165
165
  "@puckeditor/core": "^0.21.1",
166
166
  "@swc/cli": "^0.6.0",
167
167
  "@swc/core": "^1.15.11",
168
- "@types/node": "^25.1.0",
169
- "@types/react": "^19.2.10",
168
+ "@types/node": "^25.3.0",
169
+ "@types/react": "^19.2.14",
170
170
  "@types/react-dom": "^19.2.3",
171
171
  "copyfiles": "^2.4.1",
172
172
  "next": "^16.1.6",
173
- "payload": "^3.73.0",
173
+ "payload": "^3.77.0",
174
174
  "react": "^19.2.4",
175
175
  "typescript": "^5.9.3"
176
176
  },