@blockslides/ai-context 0.2.0 → 0.3.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/dist/index.cjs +985 -996
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +817 -300
- package/dist/index.d.ts +817 -300
- package/dist/index.js +985 -994
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/bundles/v1/all.ts +0 -1
- package/src/bundles/v1/allContexts.ts +1 -1
- package/src/bundles/v1/minimalCreate.ts +2 -2
- package/src/contexts/v1/blockquote.ts +10 -5
- package/src/contexts/v1/bulletList.ts +10 -5
- package/src/contexts/v1/codeBlock.ts +11 -3
- package/src/contexts/v1/column.ts +34 -14
- package/src/contexts/v1/columnGroup.ts +44 -0
- package/src/contexts/v1/core.ts +24 -4
- package/src/contexts/v1/editingRules.ts +5 -5
- package/src/contexts/v1/heading.ts +11 -4
- package/src/contexts/v1/horizontalRule.ts +9 -4
- package/src/contexts/v1/imageBlock.ts +31 -22
- package/src/contexts/v1/index.ts +1 -1
- package/src/contexts/v1/paragraph.ts +11 -5
- package/src/contexts/v1/slide.ts +5 -1
- package/src/contexts/v1/youtube.ts +14 -7
- package/src/index.ts +0 -3
- package/src/schemas/v1/blockquote.schema.json +13 -2
- package/src/schemas/v1/bulletList.schema.json +13 -2
- package/src/schemas/v1/codeBlock.schema.json +12 -3
- package/src/schemas/v1/column.schema.json +18 -14
- package/src/schemas/v1/columnGroup.schema.json +45 -0
- package/src/schemas/v1/heading.schema.json +12 -7
- package/src/schemas/v1/horizontalRule.schema.json +7 -2
- package/src/schemas/v1/imageBlock.schema.json +25 -15
- package/src/schemas/v1/index.ts +1 -1
- package/src/schemas/v1/paragraph.schema.json +13 -2
- package/src/schemas/v1/slide.schema.json +6 -0
- package/src/schemas/v1/youtube.schema.json +9 -6
- package/src/templates/v1/presetTemplateBuilder.ts +401 -443
- package/src/templates/v1/schemaBuilder.ts +195 -263
- package/src/types/v1.ts +40 -25
- package/src/contexts/v1/row.ts +0 -25
- package/src/examples/v1/flyers.ts +0 -30
- package/src/examples/v1/index.ts +0 -4
- package/src/examples/v1/slides.ts +0 -31
- package/src/recipes/v1/addTwoColumns.ts +0 -13
- package/src/recipes/v1/createSlide.ts +0 -29
- package/src/recipes/v1/editImageToCover.ts +0 -13
- package/src/recipes/v1/index.ts +0 -5
- package/src/schemas/v1/row.schema.json +0 -29
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// TODO: Add additional block helpers (tables, marks) to cover all extensions.
|
|
2
|
-
// TODO: Add more slide presets (hero, imageCover, agenda) once shapes are finalized.
|
|
3
2
|
// TODO: Add server-side validation helper that checks slides against schemasV1.
|
|
4
3
|
// TODO: Add tests/examples that demonstrate agent/tool usage.
|
|
5
4
|
|
|
@@ -7,13 +6,13 @@ import type { JSONContent } from "@blockslides/core";
|
|
|
7
6
|
import type {
|
|
8
7
|
SizeKey,
|
|
9
8
|
SlideAttrs,
|
|
10
|
-
RowAttrs,
|
|
11
9
|
ColumnAttrs,
|
|
12
10
|
ImageBlockAttrs,
|
|
11
|
+
BaseBlockAttrs,
|
|
13
12
|
} from "../../types/v1";
|
|
14
13
|
|
|
15
14
|
type Block = JSONContent;
|
|
16
|
-
type SlideNode = JSONContent; // slide ->
|
|
15
|
+
type SlideNode = JSONContent; // slide -> block+
|
|
17
16
|
type TextMark = { type: string; attrs?: Record<string, any> };
|
|
18
17
|
|
|
19
18
|
const defaults = {
|
|
@@ -22,16 +21,8 @@ const defaults = {
|
|
|
22
21
|
size: attrs?.size ?? ("16x9" as SizeKey),
|
|
23
22
|
className: attrs?.className ?? "",
|
|
24
23
|
}),
|
|
25
|
-
row: (attrs?: Partial<RowAttrs>): RowAttrs => ({
|
|
26
|
-
layout: attrs?.layout ?? "1",
|
|
27
|
-
className: attrs?.className ?? "",
|
|
28
|
-
}),
|
|
29
24
|
column: (attrs?: Partial<ColumnAttrs>): ColumnAttrs => ({
|
|
30
|
-
|
|
31
|
-
contentMode: attrs?.contentMode ?? "default",
|
|
32
|
-
verticalAlign: attrs?.verticalAlign,
|
|
33
|
-
horizontalAlign: attrs?.horizontalAlign,
|
|
34
|
-
padding: attrs?.padding,
|
|
25
|
+
...attrs,
|
|
35
26
|
}),
|
|
36
27
|
};
|
|
37
28
|
|
|
@@ -116,87 +107,115 @@ export const blocks = {
|
|
|
116
107
|
type: "youtube",
|
|
117
108
|
attrs,
|
|
118
109
|
}),
|
|
110
|
+
|
|
111
|
+
column: (content: Block[], attrs?: Partial<ColumnAttrs>) => ({
|
|
112
|
+
type: "column" as const,
|
|
113
|
+
attrs: attrs ?? {},
|
|
114
|
+
content,
|
|
115
|
+
}),
|
|
116
|
+
|
|
117
|
+
columnGroup: (columns: Block[], attrs?: { layout?: string; fill?: boolean; className?: string }) => ({
|
|
118
|
+
type: "columnGroup" as const,
|
|
119
|
+
attrs: attrs ?? {},
|
|
120
|
+
content: columns,
|
|
121
|
+
}),
|
|
119
122
|
};
|
|
120
123
|
|
|
121
124
|
type SingleColOpts = {
|
|
122
125
|
slideAttrs?: Partial<SlideAttrs>;
|
|
123
|
-
rowAttrs?: Partial<RowAttrs>;
|
|
124
126
|
columnAttrs?: Partial<ColumnAttrs>;
|
|
125
127
|
content?: Block[];
|
|
126
128
|
};
|
|
127
129
|
|
|
130
|
+
type ColumnNode = {
|
|
131
|
+
type: "column";
|
|
132
|
+
attrs?: Partial<ColumnAttrs>;
|
|
133
|
+
content: Block[];
|
|
134
|
+
};
|
|
135
|
+
|
|
128
136
|
type TwoColOpts = {
|
|
129
137
|
slideAttrs?: Partial<SlideAttrs>;
|
|
130
|
-
|
|
131
|
-
leftColumnAttrs?: Partial<ColumnAttrs>;
|
|
132
|
-
rightColumnAttrs?: Partial<ColumnAttrs>;
|
|
133
|
-
left?: Block[];
|
|
134
|
-
right?: Block[];
|
|
138
|
+
columns: [ColumnNode, ColumnNode];
|
|
135
139
|
};
|
|
136
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Slide builder with presets for common layouts.
|
|
143
|
+
*
|
|
144
|
+
* New schema: slides contain blocks directly (doc → slide+ → block+).
|
|
145
|
+
* Adjacent columns automatically form horizontal layouts via CSS.
|
|
146
|
+
*/
|
|
137
147
|
export const slide = Object.assign(
|
|
148
|
+
/**
|
|
149
|
+
* Create a slide with direct block content (no column wrapper).
|
|
150
|
+
*/
|
|
138
151
|
(opts: {
|
|
139
152
|
slideAttrs?: Partial<SlideAttrs>;
|
|
140
|
-
|
|
141
|
-
columnAttrs?: Partial<ColumnAttrs>;
|
|
153
|
+
content?: Block[];
|
|
142
154
|
} = {}): SlideNode => ({
|
|
143
155
|
type: "slide",
|
|
144
156
|
attrs: defaults.slide(opts.slideAttrs),
|
|
145
|
-
content: [
|
|
146
|
-
{
|
|
147
|
-
type: "row",
|
|
148
|
-
attrs: defaults.row(opts.rowAttrs),
|
|
149
|
-
content: [
|
|
150
|
-
{
|
|
151
|
-
type: "column",
|
|
152
|
-
attrs: defaults.column(opts.columnAttrs),
|
|
153
|
-
content: [],
|
|
154
|
-
},
|
|
155
|
-
],
|
|
156
|
-
},
|
|
157
|
-
],
|
|
157
|
+
content: opts.content ?? [],
|
|
158
158
|
}),
|
|
159
159
|
{
|
|
160
|
+
/**
|
|
161
|
+
* Single column layout - wraps content in one column block.
|
|
162
|
+
*/
|
|
160
163
|
singleCol: (opts: SingleColOpts = {}): SlideNode => ({
|
|
161
164
|
type: "slide",
|
|
162
165
|
attrs: defaults.slide(opts.slideAttrs),
|
|
163
166
|
content: [
|
|
164
167
|
{
|
|
165
|
-
type: "
|
|
166
|
-
attrs: defaults.
|
|
167
|
-
content: [
|
|
168
|
-
{
|
|
169
|
-
type: "column",
|
|
170
|
-
attrs: defaults.column(opts.columnAttrs),
|
|
171
|
-
content: opts.content ?? [],
|
|
172
|
-
},
|
|
173
|
-
],
|
|
168
|
+
type: "column",
|
|
169
|
+
attrs: defaults.column(opts.columnAttrs),
|
|
170
|
+
content: opts.content ?? [],
|
|
174
171
|
},
|
|
175
172
|
],
|
|
176
173
|
}),
|
|
177
174
|
|
|
178
|
-
|
|
175
|
+
/**
|
|
176
|
+
* Two column layout - columns grouped side-by-side via columnGroup.
|
|
177
|
+
* Pass two column objects created with blocks.column().
|
|
178
|
+
*/
|
|
179
|
+
twoCol: (col1: ColumnNode, col2: ColumnNode, slideAttrs?: Partial<SlideAttrs>): SlideNode => ({
|
|
179
180
|
type: "slide",
|
|
180
|
-
attrs: defaults.slide(
|
|
181
|
+
attrs: defaults.slide(slideAttrs),
|
|
181
182
|
content: [
|
|
182
183
|
{
|
|
183
|
-
type: "
|
|
184
|
-
attrs:
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
184
|
+
type: "columnGroup",
|
|
185
|
+
attrs: { fill: true },
|
|
186
|
+
content: [col1, col2],
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
}),
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Three column layout - columns grouped side-by-side via columnGroup.
|
|
193
|
+
* Pass three column objects created with blocks.column().
|
|
194
|
+
*/
|
|
195
|
+
threeCol: (col1: ColumnNode, col2: ColumnNode, col3: ColumnNode, slideAttrs?: Partial<SlideAttrs>): SlideNode => ({
|
|
196
|
+
type: "slide",
|
|
197
|
+
attrs: defaults.slide(slideAttrs),
|
|
198
|
+
content: [
|
|
199
|
+
{
|
|
200
|
+
type: "columnGroup",
|
|
201
|
+
attrs: { fill: true },
|
|
202
|
+
content: [col1, col2, col3],
|
|
203
|
+
},
|
|
204
|
+
],
|
|
205
|
+
}),
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Four column layout - columns grouped side-by-side via columnGroup.
|
|
209
|
+
* Pass four column objects created with blocks.column().
|
|
210
|
+
*/
|
|
211
|
+
fourCol: (col1: ColumnNode, col2: ColumnNode, col3: ColumnNode, col4: ColumnNode, slideAttrs?: Partial<SlideAttrs>): SlideNode => ({
|
|
212
|
+
type: "slide",
|
|
213
|
+
attrs: defaults.slide(slideAttrs),
|
|
214
|
+
content: [
|
|
215
|
+
{
|
|
216
|
+
type: "columnGroup",
|
|
217
|
+
attrs: { fill: true },
|
|
218
|
+
content: [col1, col2, col3, col4],
|
|
200
219
|
},
|
|
201
220
|
],
|
|
202
221
|
}),
|
|
@@ -206,7 +225,6 @@ export const slide = Object.assign(
|
|
|
206
225
|
// Slide layout presets beyond the base helpers
|
|
207
226
|
type HeroOpts = {
|
|
208
227
|
slideAttrs?: Partial<SlideAttrs>;
|
|
209
|
-
rowAttrs?: Partial<RowAttrs>;
|
|
210
228
|
columnAttrs?: Partial<ColumnAttrs>;
|
|
211
229
|
content?: Block[];
|
|
212
230
|
};
|
|
@@ -216,26 +234,22 @@ type ImageCoverOpts = {
|
|
|
216
234
|
image?: ImageBlockAttrs;
|
|
217
235
|
overlay?: Block[];
|
|
218
236
|
columnAttrs?: Partial<ColumnAttrs>;
|
|
219
|
-
rowAttrs?: Partial<RowAttrs>;
|
|
220
237
|
};
|
|
221
238
|
|
|
222
239
|
type QuoteOpts = {
|
|
223
240
|
slideAttrs?: Partial<SlideAttrs>;
|
|
224
|
-
rowAttrs?: Partial<RowAttrs>;
|
|
225
241
|
columnAttrs?: Partial<ColumnAttrs>;
|
|
226
242
|
quote?: Block[];
|
|
227
243
|
};
|
|
228
244
|
|
|
229
245
|
type AgendaOpts = {
|
|
230
246
|
slideAttrs?: Partial<SlideAttrs>;
|
|
231
|
-
rowAttrs?: Partial<RowAttrs>;
|
|
232
247
|
columnAttrs?: Partial<ColumnAttrs>;
|
|
233
248
|
items?: (string | Block)[];
|
|
234
249
|
};
|
|
235
250
|
|
|
236
251
|
type MultiColOpts = {
|
|
237
252
|
slideAttrs?: Partial<SlideAttrs>;
|
|
238
|
-
rowAttrs?: Partial<RowAttrs>;
|
|
239
253
|
columns: {
|
|
240
254
|
content?: Block[];
|
|
241
255
|
attrs?: Partial<ColumnAttrs>;
|
|
@@ -244,7 +258,6 @@ type MultiColOpts = {
|
|
|
244
258
|
|
|
245
259
|
type MediaTextOpts = {
|
|
246
260
|
slideAttrs?: Partial<SlideAttrs>;
|
|
247
|
-
rowAttrs?: Partial<RowAttrs>;
|
|
248
261
|
media?: Block[];
|
|
249
262
|
text?: Block[];
|
|
250
263
|
mediaColumnAttrs?: Partial<ColumnAttrs>;
|
|
@@ -253,16 +266,17 @@ type MediaTextOpts = {
|
|
|
253
266
|
|
|
254
267
|
type Stack2Opts = {
|
|
255
268
|
slideAttrs?: Partial<SlideAttrs>;
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
columns: { content?: Block[]; attrs?: Partial<ColumnAttrs> }[];
|
|
259
|
-
};
|
|
260
|
-
bottomRow?: {
|
|
261
|
-
rowAttrs?: Partial<RowAttrs>;
|
|
262
|
-
columns: { content?: Block[]; attrs?: Partial<ColumnAttrs> }[];
|
|
263
|
-
};
|
|
269
|
+
topColumns?: { content?: Block[]; attrs?: Partial<ColumnAttrs> }[];
|
|
270
|
+
bottomColumns?: { content?: Block[]; attrs?: Partial<ColumnAttrs> }[];
|
|
264
271
|
};
|
|
265
272
|
|
|
273
|
+
/** Helper to create a column block with attrs and content */
|
|
274
|
+
const column = (attrs: Partial<ColumnAttrs> | undefined, content: Block[] = []): Block => ({
|
|
275
|
+
type: "column",
|
|
276
|
+
attrs: defaults.column(attrs),
|
|
277
|
+
content,
|
|
278
|
+
});
|
|
279
|
+
|
|
266
280
|
type TemplatePreset =
|
|
267
281
|
| "slide.empty"
|
|
268
282
|
| "slide.singleCol"
|
|
@@ -283,7 +297,6 @@ type TemplatePreset =
|
|
|
283
297
|
type CreateTemplateInput = {
|
|
284
298
|
preset: TemplatePreset;
|
|
285
299
|
slideAttrs?: Partial<SlideAttrs>;
|
|
286
|
-
rowAttrs?: Partial<RowAttrs>;
|
|
287
300
|
columnAttrs?: Partial<ColumnAttrs>;
|
|
288
301
|
leftColumnAttrs?: Partial<ColumnAttrs>;
|
|
289
302
|
rightColumnAttrs?: Partial<ColumnAttrs>;
|
|
@@ -299,61 +312,38 @@ type CreateTemplateInput = {
|
|
|
299
312
|
stack2Opts?: Stack2Opts;
|
|
300
313
|
};
|
|
301
314
|
|
|
302
|
-
const column = (attrs: Partial<ColumnAttrs> | undefined, content: Block[] = []): Block => ({
|
|
303
|
-
type: "column",
|
|
304
|
-
attrs: defaults.column(attrs),
|
|
305
|
-
content,
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
const row = (layout: RowAttrs["layout"], content: Block[], attrs?: Partial<RowAttrs>): Block => ({
|
|
309
|
-
type: "row",
|
|
310
|
-
attrs: defaults.row({ layout, ...attrs }),
|
|
311
|
-
content,
|
|
312
|
-
});
|
|
313
|
-
|
|
314
315
|
export const createTemplate = (input: CreateTemplateInput): SlideNode => {
|
|
315
316
|
switch (input.preset) {
|
|
316
317
|
case "slide.empty":
|
|
317
318
|
return slide({
|
|
318
319
|
slideAttrs: input.slideAttrs,
|
|
319
|
-
|
|
320
|
-
columnAttrs: input.columnAttrs,
|
|
320
|
+
content: [],
|
|
321
321
|
});
|
|
322
322
|
case "slide.singleCol":
|
|
323
323
|
return slide.singleCol({
|
|
324
324
|
slideAttrs: input.slideAttrs,
|
|
325
|
-
rowAttrs: input.rowAttrs,
|
|
326
325
|
columnAttrs: input.columnAttrs,
|
|
327
326
|
content: input.content ?? [],
|
|
328
327
|
});
|
|
329
328
|
case "slide.twoCol":
|
|
330
|
-
return slide.twoCol(
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
left: input.left ?? [],
|
|
336
|
-
right: input.right ?? [],
|
|
337
|
-
});
|
|
329
|
+
return slide.twoCol(
|
|
330
|
+
blocks.column(input.left ?? [], input.leftColumnAttrs),
|
|
331
|
+
blocks.column(input.right ?? [], input.rightColumnAttrs),
|
|
332
|
+
input.slideAttrs
|
|
333
|
+
);
|
|
338
334
|
case "slide.hero": {
|
|
339
335
|
const opts = input.heroOpts ?? {};
|
|
340
336
|
return {
|
|
341
337
|
type: "slide",
|
|
342
|
-
attrs: defaults.slide({
|
|
338
|
+
attrs: defaults.slide({ ...opts.slideAttrs, backgroundColor: opts.slideAttrs?.backgroundColor ?? "#020617" }),
|
|
343
339
|
content: [
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
[
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
blocks.paragraph("Subhead goes here."),
|
|
352
|
-
blocks.paragraph("Add supporting details here."),
|
|
353
|
-
]
|
|
354
|
-
),
|
|
355
|
-
],
|
|
356
|
-
{ className: "min-h-[720px] items-center justify-center p-12", ...opts.rowAttrs }
|
|
340
|
+
column(
|
|
341
|
+
{ justify: "center", align: "center", padding: "lg", fill: true, ...opts.columnAttrs },
|
|
342
|
+
opts.content ?? [
|
|
343
|
+
blocks.heading("Your headline", 1),
|
|
344
|
+
blocks.paragraph("Subhead goes here."),
|
|
345
|
+
blocks.paragraph("Add supporting details here."),
|
|
346
|
+
]
|
|
357
347
|
),
|
|
358
348
|
],
|
|
359
349
|
};
|
|
@@ -362,34 +352,19 @@ export const createTemplate = (input: CreateTemplateInput): SlideNode => {
|
|
|
362
352
|
const opts = input.imageCoverOpts ?? {};
|
|
363
353
|
const image = opts.image ?? {
|
|
364
354
|
src: "https://placehold.co/1600x900/png",
|
|
365
|
-
|
|
366
|
-
fullBleed: true,
|
|
367
|
-
align: "center",
|
|
355
|
+
size: "fill",
|
|
368
356
|
};
|
|
369
357
|
const overlay = opts.overlay ?? [blocks.heading("Overlay title", 1)];
|
|
370
358
|
return {
|
|
371
359
|
type: "slide",
|
|
372
|
-
attrs: defaults.slide({
|
|
360
|
+
attrs: defaults.slide({ ...opts.slideAttrs, backgroundColor: opts.slideAttrs?.backgroundColor ?? "#000000" }),
|
|
373
361
|
content: [
|
|
374
|
-
|
|
375
|
-
|
|
362
|
+
column(
|
|
363
|
+
{ fill: true, padding: "none", ...opts.columnAttrs },
|
|
376
364
|
[
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
content: [
|
|
381
|
-
blocks.imageBlock(image),
|
|
382
|
-
...overlay.map((node) => ({
|
|
383
|
-
...node,
|
|
384
|
-
attrs: {
|
|
385
|
-
...(node as any).attrs,
|
|
386
|
-
className: `${((node as any).attrs?.className ?? "")} absolute bottom-12 left-12 drop-shadow-lg`.trim(),
|
|
387
|
-
},
|
|
388
|
-
})),
|
|
389
|
-
],
|
|
390
|
-
},
|
|
391
|
-
],
|
|
392
|
-
{ className: "min-h-[720px]", ...opts.rowAttrs }
|
|
365
|
+
blocks.imageBlock(image),
|
|
366
|
+
...overlay,
|
|
367
|
+
]
|
|
393
368
|
),
|
|
394
369
|
],
|
|
395
370
|
};
|
|
@@ -398,24 +373,18 @@ export const createTemplate = (input: CreateTemplateInput): SlideNode => {
|
|
|
398
373
|
const opts = input.quoteOpts ?? {};
|
|
399
374
|
return {
|
|
400
375
|
type: "slide",
|
|
401
|
-
attrs: defaults.slide({
|
|
376
|
+
attrs: defaults.slide({ ...opts.slideAttrs, backgroundColor: opts.slideAttrs?.backgroundColor ?? "#ffffff" }),
|
|
402
377
|
content: [
|
|
403
|
-
|
|
404
|
-
|
|
378
|
+
column(
|
|
379
|
+
{ justify: "center", align: "center", padding: "lg", gap: "md", fill: true, ...opts.columnAttrs },
|
|
405
380
|
[
|
|
406
|
-
|
|
407
|
-
opts.
|
|
408
|
-
|
|
409
|
-
blocks.
|
|
410
|
-
opts.quote ?? [
|
|
411
|
-
blocks.paragraph("“Add your quote here.”"),
|
|
412
|
-
blocks.paragraph("— Author"),
|
|
413
|
-
]
|
|
414
|
-
),
|
|
381
|
+
blocks.blockquote(
|
|
382
|
+
opts.quote ?? [
|
|
383
|
+
blocks.paragraph("Add your quote here."),
|
|
384
|
+
blocks.paragraph("— Author"),
|
|
415
385
|
]
|
|
416
386
|
),
|
|
417
|
-
]
|
|
418
|
-
{ className: "min-h-[640px] items-center justify-center", ...opts.rowAttrs }
|
|
387
|
+
]
|
|
419
388
|
),
|
|
420
389
|
],
|
|
421
390
|
};
|
|
@@ -424,20 +393,14 @@ export const createTemplate = (input: CreateTemplateInput): SlideNode => {
|
|
|
424
393
|
const opts = input.agendaOpts ?? {};
|
|
425
394
|
return {
|
|
426
395
|
type: "slide",
|
|
427
|
-
attrs: defaults.slide({
|
|
396
|
+
attrs: defaults.slide({ ...opts.slideAttrs, backgroundColor: opts.slideAttrs?.backgroundColor ?? "#ffffff" }),
|
|
428
397
|
content: [
|
|
429
|
-
|
|
430
|
-
|
|
398
|
+
column(
|
|
399
|
+
{ padding: "lg", gap: "lg", ...opts.columnAttrs },
|
|
431
400
|
[
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
blocks.heading("Agenda", 1),
|
|
436
|
-
blocks.bulletList(opts.items ?? ["Topic 1", "Topic 2", "Topic 3"]),
|
|
437
|
-
]
|
|
438
|
-
),
|
|
439
|
-
],
|
|
440
|
-
{ className: "min-h-[640px]", ...opts.rowAttrs }
|
|
401
|
+
blocks.heading("Agenda", 1),
|
|
402
|
+
blocks.bulletList(opts.items ?? ["Topic 1", "Topic 2", "Topic 3"]),
|
|
403
|
+
]
|
|
441
404
|
),
|
|
442
405
|
],
|
|
443
406
|
};
|
|
@@ -454,14 +417,8 @@ export const createTemplate = (input: CreateTemplateInput): SlideNode => {
|
|
|
454
417
|
];
|
|
455
418
|
return {
|
|
456
419
|
type: "slide",
|
|
457
|
-
attrs: defaults.slide({
|
|
458
|
-
content: [
|
|
459
|
-
row(
|
|
460
|
-
opts.rowAttrs?.layout ?? "1-1-1",
|
|
461
|
-
cols.map((c) => column(c.attrs, c.content ?? [])),
|
|
462
|
-
{ className: "p-8 gap-4", ...opts.rowAttrs }
|
|
463
|
-
),
|
|
464
|
-
],
|
|
420
|
+
attrs: defaults.slide({ ...opts.slideAttrs, backgroundColor: opts.slideAttrs?.backgroundColor ?? "#ffffff" }),
|
|
421
|
+
content: cols.map((c) => column({ padding: "md", gap: "sm", fill: true, ...c.attrs }, c.content ?? [])),
|
|
465
422
|
};
|
|
466
423
|
}
|
|
467
424
|
case "slide.grid4": {
|
|
@@ -477,30 +434,18 @@ export const createTemplate = (input: CreateTemplateInput): SlideNode => {
|
|
|
477
434
|
];
|
|
478
435
|
return {
|
|
479
436
|
type: "slide",
|
|
480
|
-
attrs: defaults.slide({
|
|
481
|
-
content: [
|
|
482
|
-
row(
|
|
483
|
-
opts.rowAttrs?.layout ?? "1-1-1-1",
|
|
484
|
-
cols.map((c) => column(c.attrs, c.content ?? [])),
|
|
485
|
-
{ className: "p-8 gap-4", ...opts.rowAttrs }
|
|
486
|
-
),
|
|
487
|
-
],
|
|
437
|
+
attrs: defaults.slide({ ...opts.slideAttrs, backgroundColor: opts.slideAttrs?.backgroundColor ?? "#ffffff" }),
|
|
438
|
+
content: cols.map((c) => column({ padding: "md", gap: "sm", fill: true, ...c.attrs }, c.content ?? [])),
|
|
488
439
|
};
|
|
489
440
|
}
|
|
490
441
|
case "slide.oneTwo": {
|
|
491
442
|
const opts = input.mediaTextOpts ?? {};
|
|
492
443
|
return {
|
|
493
444
|
type: "slide",
|
|
494
|
-
attrs: defaults.slide({
|
|
445
|
+
attrs: defaults.slide({ ...opts.slideAttrs, backgroundColor: opts.slideAttrs?.backgroundColor ?? "#ffffff" }),
|
|
495
446
|
content: [
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
[
|
|
499
|
-
column(opts.textColumnAttrs ?? { className: "p-8 gap-4" }, opts.text ?? []),
|
|
500
|
-
column(opts.mediaColumnAttrs ?? { className: "p-8 gap-4" }, opts.media ?? []),
|
|
501
|
-
],
|
|
502
|
-
{ className: "min-h-[640px]", ...opts.rowAttrs }
|
|
503
|
-
),
|
|
447
|
+
column({ padding: "md", gap: "sm", width: "33%", ...opts.textColumnAttrs }, opts.text ?? []),
|
|
448
|
+
column({ padding: "md", gap: "sm", fill: true, ...opts.mediaColumnAttrs }, opts.media ?? []),
|
|
504
449
|
],
|
|
505
450
|
};
|
|
506
451
|
}
|
|
@@ -508,16 +453,10 @@ export const createTemplate = (input: CreateTemplateInput): SlideNode => {
|
|
|
508
453
|
const opts = input.mediaTextOpts ?? {};
|
|
509
454
|
return {
|
|
510
455
|
type: "slide",
|
|
511
|
-
attrs: defaults.slide({
|
|
456
|
+
attrs: defaults.slide({ ...opts.slideAttrs, backgroundColor: opts.slideAttrs?.backgroundColor ?? "#ffffff" }),
|
|
512
457
|
content: [
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
[
|
|
516
|
-
column(opts.mediaColumnAttrs ?? { className: "p-8 gap-4" }, opts.media ?? []),
|
|
517
|
-
column(opts.textColumnAttrs ?? { className: "p-8 gap-4" }, opts.text ?? []),
|
|
518
|
-
],
|
|
519
|
-
{ className: "min-h-[640px]", ...opts.rowAttrs }
|
|
520
|
-
),
|
|
458
|
+
column({ padding: "md", gap: "sm", fill: true, ...opts.mediaColumnAttrs }, opts.media ?? []),
|
|
459
|
+
column({ padding: "md", gap: "sm", width: "33%", ...opts.textColumnAttrs }, opts.text ?? []),
|
|
521
460
|
],
|
|
522
461
|
};
|
|
523
462
|
}
|
|
@@ -533,30 +472,18 @@ export const createTemplate = (input: CreateTemplateInput): SlideNode => {
|
|
|
533
472
|
];
|
|
534
473
|
return {
|
|
535
474
|
type: "slide",
|
|
536
|
-
attrs: defaults.slide({
|
|
537
|
-
content: [
|
|
538
|
-
row(
|
|
539
|
-
opts.rowAttrs?.layout ?? "1-2-1",
|
|
540
|
-
cols.map((c) => column(c.attrs, c.content ?? [])),
|
|
541
|
-
{ className: "p-8 gap-4", ...opts.rowAttrs }
|
|
542
|
-
),
|
|
543
|
-
],
|
|
475
|
+
attrs: defaults.slide({ ...opts.slideAttrs, backgroundColor: opts.slideAttrs?.backgroundColor ?? "#ffffff" }),
|
|
476
|
+
content: cols.map((c, i) => column({ padding: "md", gap: "sm", width: i === 1 ? "50%" : "25%", ...c.attrs }, c.content ?? [])),
|
|
544
477
|
};
|
|
545
478
|
}
|
|
546
479
|
case "slide.textMedia": {
|
|
547
480
|
const opts = input.mediaTextOpts ?? {};
|
|
548
481
|
return {
|
|
549
482
|
type: "slide",
|
|
550
|
-
attrs: defaults.slide({
|
|
483
|
+
attrs: defaults.slide({ ...opts.slideAttrs, backgroundColor: opts.slideAttrs?.backgroundColor ?? "#ffffff" }),
|
|
551
484
|
content: [
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
[
|
|
555
|
-
column(opts.textColumnAttrs ?? { className: "p-10 gap-4" }, opts.text ?? []),
|
|
556
|
-
column(opts.mediaColumnAttrs ?? { className: "p-10 gap-4 bg-slate-50" }, opts.media ?? []),
|
|
557
|
-
],
|
|
558
|
-
{ className: "min-h-[640px]", ...opts.rowAttrs }
|
|
559
|
-
),
|
|
485
|
+
column({ padding: "lg", gap: "sm", fill: true, ...opts.textColumnAttrs }, opts.text ?? []),
|
|
486
|
+
column({ padding: "lg", gap: "sm", fill: true, backgroundColor: "#f8fafc", ...opts.mediaColumnAttrs }, opts.media ?? []),
|
|
560
487
|
],
|
|
561
488
|
};
|
|
562
489
|
}
|
|
@@ -564,46 +491,26 @@ export const createTemplate = (input: CreateTemplateInput): SlideNode => {
|
|
|
564
491
|
const opts = input.mediaTextOpts ?? {};
|
|
565
492
|
return {
|
|
566
493
|
type: "slide",
|
|
567
|
-
attrs: defaults.slide({
|
|
494
|
+
attrs: defaults.slide({ ...opts.slideAttrs, backgroundColor: opts.slideAttrs?.backgroundColor ?? "#ffffff" }),
|
|
568
495
|
content: [
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
[
|
|
572
|
-
column(opts.mediaColumnAttrs ?? { className: "p-10 gap-4 bg-slate-50" }, opts.media ?? []),
|
|
573
|
-
column(opts.textColumnAttrs ?? { className: "p-10 gap-4" }, opts.text ?? []),
|
|
574
|
-
],
|
|
575
|
-
{ className: "min-h-[640px]", ...opts.rowAttrs }
|
|
576
|
-
),
|
|
496
|
+
column({ padding: "lg", gap: "sm", fill: true, backgroundColor: "#f8fafc", ...opts.mediaColumnAttrs }, opts.media ?? []),
|
|
497
|
+
column({ padding: "lg", gap: "sm", fill: true, ...opts.textColumnAttrs }, opts.text ?? []),
|
|
577
498
|
],
|
|
578
499
|
};
|
|
579
500
|
}
|
|
580
501
|
case "slide.stack2": {
|
|
581
502
|
const opts = input.stack2Opts ?? {};
|
|
582
|
-
const top = opts.
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
rowAttrs: { layout: "1-1" },
|
|
588
|
-
columns: [
|
|
589
|
-
{ content: [blocks.paragraph("Left detail")] },
|
|
590
|
-
{ content: [blocks.paragraph("Right detail")] },
|
|
591
|
-
],
|
|
592
|
-
};
|
|
503
|
+
const top = opts.topColumns ?? [{ content: [blocks.heading("Title", 1), blocks.paragraph("Subhead")] }];
|
|
504
|
+
const bottom = opts.bottomColumns ?? [
|
|
505
|
+
{ content: [blocks.paragraph("Left detail")] },
|
|
506
|
+
{ content: [blocks.paragraph("Right detail")] },
|
|
507
|
+
];
|
|
593
508
|
return {
|
|
594
509
|
type: "slide",
|
|
595
|
-
attrs: defaults.slide({
|
|
510
|
+
attrs: defaults.slide({ ...opts.slideAttrs, backgroundColor: opts.slideAttrs?.backgroundColor ?? "#ffffff" }),
|
|
596
511
|
content: [
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
top.columns.map((c) => column(c.attrs, c.content ?? [])),
|
|
600
|
-
{ className: "p-8 gap-4", ...top.rowAttrs }
|
|
601
|
-
),
|
|
602
|
-
row(
|
|
603
|
-
bottom.rowAttrs?.layout ?? "1-1",
|
|
604
|
-
bottom.columns.map((c) => column(c.attrs, c.content ?? [])),
|
|
605
|
-
{ className: "p-8 gap-4", ...bottom.rowAttrs }
|
|
606
|
-
),
|
|
512
|
+
...top.map((c) => column({ padding: "md", gap: "sm", ...c.attrs }, c.content ?? [])),
|
|
513
|
+
...bottom.map((c) => column({ padding: "md", gap: "sm", ...c.attrs }, c.content ?? [])),
|
|
607
514
|
],
|
|
608
515
|
};
|
|
609
516
|
}
|
|
@@ -633,40 +540,65 @@ export const listTemplates = (): TemplatePreset[] => [
|
|
|
633
540
|
export const templatesV1Context = `
|
|
634
541
|
BlockSlides templates API (v1)
|
|
635
542
|
|
|
543
|
+
Document Hierarchy:
|
|
544
|
+
doc → slide+ → block+
|
|
545
|
+
Slides contain blocks directly - no mandatory row wrapper.
|
|
546
|
+
Adjacent columns automatically form horizontal layouts via CSS.
|
|
547
|
+
|
|
636
548
|
Presets:
|
|
637
|
-
- slide
|
|
638
|
-
- slide.singleCol({ content?, slideAttrs?,
|
|
639
|
-
- slide.twoCol(
|
|
640
|
-
- slide.
|
|
641
|
-
- slide.
|
|
642
|
-
- slide.
|
|
643
|
-
- slide.
|
|
644
|
-
- slide.
|
|
645
|
-
- slide.
|
|
646
|
-
- slide.
|
|
647
|
-
- slide.
|
|
648
|
-
|
|
649
|
-
|
|
549
|
+
- slide({ content?, slideAttrs? }): slide with direct block content
|
|
550
|
+
- slide.singleCol({ content?, slideAttrs?, columnAttrs? }): single column layout
|
|
551
|
+
- slide.twoCol(column1, column2, slideAttrs?): two columns side by side
|
|
552
|
+
- slide.threeCol(col1, col2, col3, slideAttrs?): three columns side by side
|
|
553
|
+
- slide.fourCol(col1, col2, col3, col4, slideAttrs?): four columns side by side
|
|
554
|
+
- slide.hero({ heroOpts }): centered content on dark background
|
|
555
|
+
- slide.imageCover({ imageCoverOpts }): full-bleed image with overlay
|
|
556
|
+
- slide.quote({ quoteOpts }): centered blockquote
|
|
557
|
+
- slide.agenda({ agendaOpts }): title with bullet list
|
|
558
|
+
- slide.grid3/grid4({ multiColOpts }): equal-width column grids
|
|
559
|
+
- slide.oneTwo/twoOne({ mediaTextOpts }): asymmetric layouts
|
|
560
|
+
- slide.textMedia/mediaText({ mediaTextOpts }): text and media columns
|
|
561
|
+
- slide.stack2({ stack2Opts }): stacked column sections
|
|
562
|
+
|
|
563
|
+
Base Block Attributes (available on ALL blocks):
|
|
564
|
+
- align: "left" | "center" | "right" | "stretch" - horizontal alignment
|
|
565
|
+
- justify: "start" | "center" | "end" | "space-between" - vertical distribution
|
|
566
|
+
- padding: "none" | "sm" | "md" | "lg" - internal spacing (8px/16px/32px)
|
|
567
|
+
- margin: "none" | "sm" | "md" | "lg" - external spacing
|
|
568
|
+
- gap: "none" | "sm" | "md" | "lg" - space between children
|
|
569
|
+
- backgroundColor: CSS color
|
|
570
|
+
- backgroundImage: URL
|
|
571
|
+
- borderRadius: "none" | "sm" | "md" | "lg" (4px/8px/16px)
|
|
572
|
+
- border: CSS border
|
|
573
|
+
- fill: boolean - fill available space
|
|
574
|
+
- width: CSS width
|
|
575
|
+
- height: CSS height
|
|
576
|
+
|
|
577
|
+
Block Helpers:
|
|
650
578
|
- blocks.text(text, marks?)
|
|
651
|
-
- blocks.heading(text, level?)
|
|
579
|
+
- blocks.heading(text, level?) - level 1-6
|
|
652
580
|
- blocks.paragraph(text?)
|
|
653
581
|
- blocks.bulletList([string | Block][])
|
|
654
582
|
- blocks.codeBlock(code, language?)
|
|
655
583
|
- blocks.horizontalRule()
|
|
656
584
|
- blocks.hardBreak()
|
|
657
|
-
- blocks.imageBlock({ src,
|
|
585
|
+
- blocks.imageBlock({ src, size?, crop?, alt?, caption?, credit? })
|
|
586
|
+
- size: "fill" | "fit" | "natural" - how image fills container
|
|
587
|
+
- crop: "center" | "top" | "bottom" | "left" | "right" | corner positions
|
|
658
588
|
- blocks.blockquote(content?)
|
|
659
589
|
- blocks.listItem(content?)
|
|
660
|
-
- blocks.image({ src, alt?, title?, width?, height? })
|
|
661
590
|
- blocks.youtube({ src?, start?, width?, height? })
|
|
662
591
|
|
|
663
592
|
Agent/tool usage:
|
|
664
|
-
-
|
|
665
|
-
-
|
|
593
|
+
- Use blocks.column(content, attrs) to create columns
|
|
594
|
+
- Use blocks.columnGroup(columns) to group columns horizontally
|
|
595
|
+
- Call createTemplate({ preset: "slide.twoCol", left: [...], right: [...] })
|
|
596
|
+
- Wrap returned slides in { type: "doc", content: [/* slides here */] }
|
|
666
597
|
|
|
667
598
|
Notes:
|
|
668
|
-
- Size defaults to 16x9; override via slideAttrs.size
|
|
669
|
-
-
|
|
599
|
+
- Size defaults to 16x9; override via slideAttrs.size
|
|
600
|
+
- Use fill: true on columns to distribute space evenly
|
|
601
|
+
- Use semantic spacing tokens (sm/md/lg) instead of raw pixel values
|
|
670
602
|
`.trim();
|
|
671
603
|
|
|
672
604
|
export type { Block, SlideNode, TemplatePreset, CreateTemplateInput };
|