slotdeck 0.0.0-stage → 0.1.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.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +486 -2
  3. package/dist/deck/numbers.d.ts +14 -0
  4. package/dist/deck/numbers.js +48 -0
  5. package/dist/deck/numbers.js.map +1 -0
  6. package/dist/deck/spec.d.ts +73 -0
  7. package/dist/deck/spec.js +21 -0
  8. package/dist/deck/spec.js.map +1 -0
  9. package/dist/deck/validate.d.ts +49 -0
  10. package/dist/deck/validate.js +189 -0
  11. package/dist/deck/validate.js.map +1 -0
  12. package/dist/generate.d.ts +53 -0
  13. package/dist/generate.js +44 -0
  14. package/dist/generate.js.map +1 -0
  15. package/dist/index.d.ts +42 -0
  16. package/dist/index.js +27 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/llm/gemini.d.ts +39 -0
  19. package/dist/llm/gemini.js +147 -0
  20. package/dist/llm/gemini.js.map +1 -0
  21. package/dist/llm/provider.d.ts +38 -0
  22. package/dist/llm/provider.js +2 -0
  23. package/dist/llm/provider.js.map +1 -0
  24. package/dist/node.d.ts +3 -0
  25. package/dist/node.js +31 -0
  26. package/dist/node.js.map +1 -0
  27. package/dist/plan/assets.d.ts +28 -0
  28. package/dist/plan/assets.js +80 -0
  29. package/dist/plan/assets.js.map +1 -0
  30. package/dist/plan/catalog.d.ts +106 -0
  31. package/dist/plan/catalog.js +302 -0
  32. package/dist/plan/catalog.js.map +1 -0
  33. package/dist/plan/edit.d.ts +31 -0
  34. package/dist/plan/edit.js +364 -0
  35. package/dist/plan/edit.js.map +1 -0
  36. package/dist/plan/plan.d.ts +106 -0
  37. package/dist/plan/plan.js +243 -0
  38. package/dist/plan/plan.js.map +1 -0
  39. package/dist/plan/write.d.ts +39 -0
  40. package/dist/plan/write.js +179 -0
  41. package/dist/plan/write.js.map +1 -0
  42. package/dist/pptx/package.d.ts +48 -0
  43. package/dist/pptx/package.js +202 -0
  44. package/dist/pptx/package.js.map +1 -0
  45. package/dist/pptx/presentation.d.ts +20 -0
  46. package/dist/pptx/presentation.js +31 -0
  47. package/dist/pptx/presentation.js.map +1 -0
  48. package/dist/pptx/xml.d.ts +23 -0
  49. package/dist/pptx/xml.js +67 -0
  50. package/dist/pptx/xml.js.map +1 -0
  51. package/dist/render/chart.d.ts +18 -0
  52. package/dist/render/chart.js +158 -0
  53. package/dist/render/chart.js.map +1 -0
  54. package/dist/render/extract.d.ts +11 -0
  55. package/dist/render/extract.js +65 -0
  56. package/dist/render/extract.js.map +1 -0
  57. package/dist/render/picture.d.ts +20 -0
  58. package/dist/render/picture.js +114 -0
  59. package/dist/render/picture.js.map +1 -0
  60. package/dist/render/render.d.ts +32 -0
  61. package/dist/render/render.js +247 -0
  62. package/dist/render/render.js.map +1 -0
  63. package/dist/render/repeat.d.ts +15 -0
  64. package/dist/render/repeat.js +118 -0
  65. package/dist/render/repeat.js.map +1 -0
  66. package/dist/render/table.d.ts +12 -0
  67. package/dist/render/table.js +84 -0
  68. package/dist/render/table.js.map +1 -0
  69. package/dist/render/text.d.ts +31 -0
  70. package/dist/render/text.js +210 -0
  71. package/dist/render/text.js.map +1 -0
  72. package/dist/store.d.ts +18 -0
  73. package/dist/store.js +23 -0
  74. package/dist/store.js.map +1 -0
  75. package/dist/template/change.d.ts +138 -0
  76. package/dist/template/change.js +406 -0
  77. package/dist/template/change.js.map +1 -0
  78. package/dist/template/check.d.ts +15 -0
  79. package/dist/template/check.js +97 -0
  80. package/dist/template/check.js.map +1 -0
  81. package/dist/template/draft.d.ts +47 -0
  82. package/dist/template/draft.js +276 -0
  83. package/dist/template/draft.js.map +1 -0
  84. package/dist/template/inspect.d.ts +53 -0
  85. package/dist/template/inspect.js +157 -0
  86. package/dist/template/inspect.js.map +1 -0
  87. package/dist/template/instruct.d.ts +26 -0
  88. package/dist/template/instruct.js +278 -0
  89. package/dist/template/instruct.js.map +1 -0
  90. package/dist/template/manifest.d.ts +226 -0
  91. package/dist/template/manifest.js +116 -0
  92. package/dist/template/manifest.js.map +1 -0
  93. package/dist/template/measure.d.ts +80 -0
  94. package/dist/template/measure.js +212 -0
  95. package/dist/template/measure.js.map +1 -0
  96. package/dist/template/sample.d.ts +17 -0
  97. package/dist/template/sample.js +96 -0
  98. package/dist/template/sample.js.map +1 -0
  99. package/dist/template/view.d.ts +46 -0
  100. package/dist/template/view.js +38 -0
  101. package/dist/template/view.js.map +1 -0
  102. package/dist/tools.d.ts +68 -0
  103. package/dist/tools.js +266 -0
  104. package/dist/tools.js.map +1 -0
  105. package/dist/util/image.d.ts +11 -0
  106. package/dist/util/image.js +58 -0
  107. package/dist/util/image.js.map +1 -0
  108. package/dist/util/png.d.ts +2 -0
  109. package/dist/util/png.js +50 -0
  110. package/dist/util/png.js.map +1 -0
  111. package/package.json +63 -4
@@ -0,0 +1,147 @@
1
+ export const GEMINI_DEVELOPER_ENDPOINT = 'https://generativelanguage.googleapis.com/v1/interactions';
2
+ /** Endpoint of the Interactions API on Vertex AI. Authenticate with a bearer token via `headers`. */
3
+ export function vertexEndpoint(project, location = 'global') {
4
+ const host = location === 'global' ? 'aiplatform.googleapis.com' : `${location}-aiplatform.googleapis.com`;
5
+ return `https://${host}/v1beta1/projects/${project}/locations/${location}/interactions`;
6
+ }
7
+ export class GeminiError extends Error {
8
+ status;
9
+ constructor(message, status) {
10
+ super(message);
11
+ this.name = 'GeminiError';
12
+ this.status = status;
13
+ }
14
+ }
15
+ async function createInteraction(connection, body, signal) {
16
+ const extra = typeof connection.headers === 'function' ? await connection.headers() : connection.headers;
17
+ const headers = { 'Content-Type': 'application/json', ...extra };
18
+ if (connection.apiKey)
19
+ headers['x-goog-api-key'] = connection.apiKey;
20
+ const init = { method: 'POST', headers, body: JSON.stringify(body) };
21
+ // A request can hang without failing, so every one has a limit of its own next to the caller's signal.
22
+ const limit = connection.timeoutMs ?? 180_000;
23
+ const timeout = limit > 0 ? AbortSignal.timeout(limit) : undefined;
24
+ const combined = signal && timeout ? AbortSignal.any([signal, timeout]) : (signal ?? timeout);
25
+ if (combined)
26
+ init.signal = combined;
27
+ let response;
28
+ let text;
29
+ try {
30
+ response = await (connection.fetch ?? fetch)(connection.endpoint ?? GEMINI_DEVELOPER_ENDPOINT, init);
31
+ text = await response.text();
32
+ }
33
+ catch (error) {
34
+ if (timeout?.aborted && !signal?.aborted)
35
+ throw new GeminiError(`Gemini did not answer within ${Math.round(limit / 1000)} seconds`);
36
+ throw error;
37
+ }
38
+ let parsed;
39
+ try {
40
+ parsed = JSON.parse(text);
41
+ }
42
+ catch {
43
+ parsed = undefined;
44
+ }
45
+ if (!response.ok) {
46
+ const message = parsed?.error?.message ?? text.slice(0, 300);
47
+ throw new GeminiError(`Gemini request failed (${response.status}): ${message}`, response.status);
48
+ }
49
+ const interaction = (parsed ?? {});
50
+ if (interaction.status !== 'completed') {
51
+ const reason = interaction.errors?.map((error) => error.message).filter(Boolean).join('; ');
52
+ throw new GeminiError(`Gemini interaction ended with status "${interaction.status ?? 'unknown'}"${reason ? `: ${reason}` : ''}`);
53
+ }
54
+ return interaction;
55
+ }
56
+ function outputBlocks(interaction) {
57
+ return (interaction.steps ?? []).filter((step) => step.type === 'model_output').flatMap((step) => step.content ?? []);
58
+ }
59
+ /** A Gemini text model as an {@link LlmProvider}. */
60
+ export function gemini(options) {
61
+ return {
62
+ async generateJson(request) {
63
+ const generationConfig = {};
64
+ if (options.thinkingLevel)
65
+ generationConfig.thinking_level = options.thinkingLevel;
66
+ if (options.maxOutputTokens)
67
+ generationConfig.max_output_tokens = options.maxOutputTokens;
68
+ // With pictures, the input becomes one user step holding the text and each picture.
69
+ const input = request.images?.length
70
+ ? [
71
+ {
72
+ type: 'user_input',
73
+ content: [
74
+ { type: 'text', text: request.prompt },
75
+ ...request.images.map((image) => ({ type: 'image', mime_type: image.mimeType, data: toBase64(image.data) })),
76
+ ],
77
+ },
78
+ ]
79
+ : request.prompt;
80
+ const body = {
81
+ model: options.model,
82
+ input,
83
+ system_instruction: request.system,
84
+ response_format: { type: 'text', mime_type: 'application/json', schema: request.schema },
85
+ // One-shot calls: nothing to chain to, so nothing needs to be kept server-side.
86
+ store: false,
87
+ };
88
+ if (Object.keys(generationConfig).length > 0)
89
+ body.generation_config = generationConfig;
90
+ const interaction = await createInteraction(options, body, request.signal);
91
+ const text = outputBlocks(interaction)
92
+ .filter((block) => block.type === 'text')
93
+ .map((block) => block.text ?? '')
94
+ .join('');
95
+ try {
96
+ return JSON.parse(text);
97
+ }
98
+ catch {
99
+ throw new GeminiError(`Gemini did not return valid JSON: ${text.slice(0, 200)}`);
100
+ }
101
+ },
102
+ };
103
+ }
104
+ function toBase64(data) {
105
+ let binary = '';
106
+ for (let offset = 0; offset < data.length; offset += 0x8000)
107
+ binary += String.fromCharCode(...data.subarray(offset, offset + 0x8000));
108
+ return btoa(binary);
109
+ }
110
+ const ASPECT_RATIOS = ['1:1', '2:3', '3:2', '3:4', '4:3', '4:5', '5:4', '9:16', '16:9', '21:9', '1:8', '8:1', '1:4', '4:1'];
111
+ /** The supported output ratio closest to the frame (compared on a log scale, so 2:1 and 1:2 are equally far from 1:1). */
112
+ export function nearestAspectRatio(ratio) {
113
+ let best = '1:1';
114
+ let bestDistance = Infinity;
115
+ for (const candidate of ASPECT_RATIOS) {
116
+ const [w, h] = candidate.split(':').map(Number);
117
+ const distance = Math.abs(Math.log(ratio) - Math.log(w / h));
118
+ if (distance < bestDistance) {
119
+ best = candidate;
120
+ bestDistance = distance;
121
+ }
122
+ }
123
+ return best;
124
+ }
125
+ /** A Gemini image model as an {@link ImageProvider}. */
126
+ export function geminiImages(options) {
127
+ return {
128
+ async generateImage(request) {
129
+ const prompt = options.style ? `${request.prompt}\n\n${options.style}` : request.prompt;
130
+ const body = {
131
+ model: options.model,
132
+ input: [{ type: 'user_input', content: [{ type: 'text', text: prompt }] }],
133
+ response_format: [
134
+ { type: 'text' },
135
+ { type: 'image', aspect_ratio: nearestAspectRatio(request.aspectRatio), image_size: options.imageSize ?? '1K' },
136
+ ],
137
+ store: false,
138
+ };
139
+ const interaction = await createInteraction(options, body, request.signal);
140
+ const image = outputBlocks(interaction).find((block) => block.type === 'image' && block.data);
141
+ if (!image?.data)
142
+ throw new GeminiError('Gemini returned no image');
143
+ return Uint8Array.from(atob(image.data), (char) => char.charCodeAt(0));
144
+ },
145
+ };
146
+ }
147
+ //# sourceMappingURL=gemini.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/llm/gemini.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import type { ImageKind } from '../template/manifest.ts';
2
+ /** A JSON Schema object. The planner only uses a small, widely supported subset. */
3
+ export type JsonSchema = {
4
+ [key: string]: unknown;
5
+ };
6
+ export interface JsonRequest {
7
+ system: string;
8
+ prompt: string;
9
+ /** Schema the answer must follow. Providers without schema support may treat it as guidance. */
10
+ schema: JsonSchema;
11
+ /** Pictures to look at alongside the prompt, e.g. a rendered slide. A provider that cannot see may ignore them. */
12
+ images?: Array<{
13
+ mimeType: string;
14
+ data: Uint8Array;
15
+ }>;
16
+ signal?: AbortSignal;
17
+ }
18
+ /**
19
+ * The one thing slotdeck needs from a language model: answer a prompt with JSON.
20
+ * Implement this to plug in any model or gateway.
21
+ */
22
+ export interface LlmProvider {
23
+ /** Resolves with the parsed JSON value. */
24
+ generateJson(request: JsonRequest): Promise<unknown>;
25
+ }
26
+ export interface ImageRequest {
27
+ /** Description of the picture, written by the planning model. */
28
+ prompt: string;
29
+ kind: ImageKind;
30
+ /** Width divided by height of the frame the picture goes into. */
31
+ aspectRatio: number;
32
+ signal?: AbortSignal;
33
+ }
34
+ /** Produces a picture for a slot. Implement this for any image model, stock library or search. */
35
+ export interface ImageProvider {
36
+ /** Resolves with PNG, JPEG or GIF bytes. */
37
+ generateImage(request: ImageRequest): Promise<Uint8Array>;
38
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/llm/provider.ts"],"names":[],"mappings":""}
package/dist/node.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { BlobStore } from './store.ts';
2
+ /** A {@link BlobStore} backed by a folder, e.g. a mounted volume. Keys become file paths under it. */
3
+ export declare function directoryStore(directory: string): BlobStore;
package/dist/node.js ADDED
@@ -0,0 +1,31 @@
1
+ // Node-only helpers. The main entry point stays free of Node APIs so it can run in a browser.
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
+ import { dirname, join, resolve, sep } from 'node:path';
4
+ /** A {@link BlobStore} backed by a folder, e.g. a mounted volume. Keys become file paths under it. */
5
+ export function directoryStore(directory) {
6
+ const root = resolve(directory);
7
+ const locate = (key) => {
8
+ const file = resolve(join(root, key));
9
+ if (file !== root && !file.startsWith(root + sep))
10
+ throw new Error(`Store key leaves the store folder: ${key}`);
11
+ return file;
12
+ };
13
+ return {
14
+ async get(key) {
15
+ try {
16
+ return new Uint8Array(await readFile(locate(key)));
17
+ }
18
+ catch (error) {
19
+ if (error.code === 'ENOENT')
20
+ return undefined;
21
+ throw error;
22
+ }
23
+ },
24
+ async set(key, data) {
25
+ const file = locate(key);
26
+ await mkdir(dirname(file), { recursive: true });
27
+ await writeFile(file, data);
28
+ },
29
+ };
30
+ }
31
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../src/node.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAGxD,sGAAsG;AACtG,MAAM,UAAU,cAAc,CAAC,SAAiB;IAC9C,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,MAAM,GAAG,CAAC,GAAW,EAAU,EAAE;QACrC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;QACtC,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,GAAG,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,sCAAsC,GAAG,EAAE,CAAC,CAAC;QAChH,OAAO,IAAI,CAAC;IACd,CAAC,CAAC;IACF,OAAO;QACL,KAAK,CAAC,GAAG,CAAC,GAAG;YACX,IAAI,CAAC;gBACH,OAAO,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACrD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;oBAAE,OAAO,SAAS,CAAC;gBACzE,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI;YACjB,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAChD,MAAM,SAAS,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC9B,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,28 @@
1
+ import type { DeckSpec } from '../deck/spec.ts';
2
+ import type { ImageProvider } from '../llm/provider.ts';
3
+ import type { Template } from '../render/render.ts';
4
+ import type { AssetDef, IconDef } from './catalog.ts';
5
+ export interface ResolveOptions {
6
+ /** Generates the pictures the deck describes by prompt. */
7
+ images?: ImageProvider;
8
+ icons?: IconDef[];
9
+ assets?: AssetDef[];
10
+ /**
11
+ * Generated pictures by asset id. Pass the same map on later calls and pictures whose
12
+ * description did not change are reused instead of generated again.
13
+ */
14
+ cache?: Map<string, Uint8Array>;
15
+ /** How many pictures to generate at once. Defaults to 3. */
16
+ concurrency?: number;
17
+ onProgress?: (event: {
18
+ stage: 'images';
19
+ done: number;
20
+ total: number;
21
+ }) => void;
22
+ signal?: AbortSignal;
23
+ }
24
+ /**
25
+ * Collects the bytes of every picture a deck refers to: supplied icons and assets as they are,
26
+ * described pictures from the cache or the image provider.
27
+ */
28
+ export declare function resolveAssets(template: Template, deck: DeckSpec, options?: ResolveOptions): Promise<Record<string, Uint8Array>>;
@@ -0,0 +1,80 @@
1
+ import { isImageRef } from '../deck/spec.js';
2
+ import { inspectTemplate } from '../template/inspect.js';
3
+ import { effectiveSlots, slotSources } from '../template/manifest.js';
4
+ const inspections = new WeakMap();
5
+ function inspect(template) {
6
+ let info = inspections.get(template.pkg);
7
+ if (!info) {
8
+ info = inspectTemplate(template.pkg);
9
+ inspections.set(template.pkg, info);
10
+ }
11
+ return info;
12
+ }
13
+ /**
14
+ * Collects the bytes of every picture a deck refers to: supplied icons and assets as they are,
15
+ * described pictures from the cache or the image provider.
16
+ */
17
+ export async function resolveAssets(template, deck, options = {}) {
18
+ const supplied = new Map();
19
+ for (const icon of options.icons ?? [])
20
+ supplied.set(icon.id, icon.data);
21
+ for (const asset of options.assets ?? [])
22
+ supplied.set(asset.id, asset.data);
23
+ const cache = options.cache ?? new Map();
24
+ const patterns = new Map(template.manifest.patterns.map((pattern) => [pattern.id, pattern]));
25
+ const resolved = {};
26
+ const pending = new Map();
27
+ const mirrored = (deck.direction ?? template.manifest.direction) !== template.manifest.direction;
28
+ deck.slides.forEach((slide, index) => {
29
+ const pattern = patterns.get(slide.pattern);
30
+ if (!pattern)
31
+ return; // rendering reports unknown patterns
32
+ const sources = slotSources(pattern, mirrored, slide.items);
33
+ for (const slot of effectiveSlots(pattern, slide.items)) {
34
+ // The frame a picture is sized for is the one it will actually be placed in.
35
+ const value = slide.slots[sources.get(slot.name) ?? slot.name];
36
+ if (slot.kind !== 'image' || !isImageRef(value))
37
+ continue;
38
+ const known = supplied.get(value.asset) ?? cache.get(value.asset);
39
+ if (known) {
40
+ resolved[value.asset] = known;
41
+ continue;
42
+ }
43
+ if (pending.has(value.asset))
44
+ continue;
45
+ const where = `slide ${index + 1} / ${slot.name}`;
46
+ if (!value.prompt)
47
+ throw new Error(`${where}: asset "${value.asset}" was not supplied and has no description to generate it from`);
48
+ if (!options.images)
49
+ throw new Error(`${where}: the deck asks for a generated picture, but no image provider was given`);
50
+ const info = inspect(template);
51
+ // A copied item has no shape of its own in the template; its frame is that of the item it copies.
52
+ const frameId = slot.copyOf ?? slot.shapeId;
53
+ const shape = info.slides[pattern.sourceSlide - 1]?.shapes.find((candidate) => candidate.id === frameId);
54
+ const width = (shape?.box.w ?? 0) * info.slideWidth;
55
+ const height = (shape?.box.h ?? 0) * info.slideHeight;
56
+ pending.set(value.asset, {
57
+ asset: value.asset,
58
+ prompt: value.prompt,
59
+ kind: slot.imageKind,
60
+ aspectRatio: width > 0 && height > 0 ? width / height : 1,
61
+ });
62
+ }
63
+ });
64
+ const queue = [...pending.values()];
65
+ const total = queue.length;
66
+ let done = 0;
67
+ const images = options.images;
68
+ const worker = async () => {
69
+ for (let job = queue.shift(); job && images; job = queue.shift()) {
70
+ const request = { prompt: job.prompt, kind: job.kind, aspectRatio: job.aspectRatio };
71
+ const data = await images.generateImage(options.signal ? { ...request, signal: options.signal } : request);
72
+ cache.set(job.asset, data);
73
+ resolved[job.asset] = data;
74
+ options.onProgress?.({ stage: 'images', done: ++done, total });
75
+ }
76
+ };
77
+ await Promise.all(Array.from({ length: Math.max(1, Math.min(options.concurrency ?? 3, total)) }, worker));
78
+ return resolved;
79
+ }
80
+ //# sourceMappingURL=assets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/plan/assets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,106 @@
1
+ import type { LayoutMode } from '../deck/validate.ts';
2
+ import type { JsonSchema } from '../llm/provider.ts';
3
+ import type { ChartSlotDef, ImageSlotDef, PatternDef, TableSlotDef, TemplateManifest, TextSlotDef } from '../template/manifest.ts';
4
+ /** An icon the app offers; the planning model picks from these by id. */
5
+ export interface IconDef {
6
+ id: string;
7
+ /** What the icon shows, e.g. "clock" or "handshake". */
8
+ description: string;
9
+ data: Uint8Array;
10
+ }
11
+ /** A ready-made picture the app supplies, e.g. a rendered Mermaid diagram or a product photo. */
12
+ export interface AssetDef {
13
+ id: string;
14
+ kind: 'photo' | 'graphic';
15
+ /** What the picture shows; the planning model decides where it fits from this. */
16
+ description: string;
17
+ data: Uint8Array;
18
+ }
19
+ export type ImageDensity = 'none' | 'low' | 'medium' | 'high';
20
+ export interface CatalogContext {
21
+ layout: LayoutMode;
22
+ /** Deck-level values the caller already has. */
23
+ fields: Record<string, string>;
24
+ icons: IconDef[];
25
+ assets: AssetDef[];
26
+ imageDensity: ImageDensity;
27
+ /** Whether patterns built around numbers may be offered. */
28
+ figures: boolean;
29
+ /** Size of body text as a factor on the template's; larger text gets smaller budgets. */
30
+ textScale: number;
31
+ /** Restrict planning to these pattern ids. */
32
+ allow?: string[];
33
+ }
34
+ /** How one manifest slot is presented to the planning model. */
35
+ export type PlannedSlot = {
36
+ slot: TextSlotDef;
37
+ mode: 'text' | 'paragraphs';
38
+ required: boolean;
39
+ } | {
40
+ slot: ImageSlotDef;
41
+ mode: 'photo' | 'icon' | 'graphic';
42
+ required: boolean;
43
+ } | {
44
+ slot: TableSlotDef;
45
+ mode: 'table';
46
+ required: boolean;
47
+ } | {
48
+ slot: ChartSlotDef;
49
+ mode: 'chart';
50
+ required: boolean;
51
+ };
52
+ export interface UsablePattern {
53
+ def: PatternDef;
54
+ /** The slots the model fills. Slots fed by deck fields, or left to the template, are not listed. */
55
+ slots: PlannedSlot[];
56
+ /** Whether using the pattern always puts a photo on the slide. */
57
+ needsPhoto: boolean;
58
+ /** For a pattern with a row of items: the number of items these slots are for. */
59
+ items?: number;
60
+ }
61
+ export interface Catalog {
62
+ patterns: UsablePattern[];
63
+ /** Patterns that cannot be used with this context, and why. */
64
+ excluded: Array<{
65
+ id: string;
66
+ reason: string;
67
+ }>;
68
+ }
69
+ /**
70
+ * Works out which slots of a pattern the model writes, or why the pattern cannot be used.
71
+ * Slots named in `skip` are already taken care of (carried over from an existing slide)
72
+ * and are neither offered nor required.
73
+ */
74
+ export declare function planPattern(def: PatternDef, context: CatalogContext, skip?: ReadonlySet<string>, items?: number): {
75
+ pattern: UsablePattern;
76
+ } | {
77
+ reason: string;
78
+ };
79
+ /** Works out which patterns can be filled with what the caller supplied, and which of their slots the model writes. */
80
+ export declare function buildCatalog(manifest: TemplateManifest, context: CatalogContext): Catalog;
81
+ /** The pattern list as the outline prompt shows it. */
82
+ export declare function describeCatalog(catalog: Catalog, textScale?: number): string;
83
+ /** One slide the model is asked to write. */
84
+ export interface WriteItem {
85
+ /** 1-based position of the slide in the finished deck. */
86
+ number: number;
87
+ pattern: UsablePattern;
88
+ /** What the slide is for, or what to change on it. */
89
+ purpose: string;
90
+ }
91
+ /**
92
+ * Schema for the content of a fixed set of slides: one property per slide (`s1`, `s2`, ...),
93
+ * each holding exactly that pattern's slots. Having no unions keeps it small and easy to enforce.
94
+ */
95
+ export declare function contentSchema(items: WriteItem[], context: CatalogContext): JsonSchema;
96
+ export declare function outlineSchema(catalog: Catalog, count: {
97
+ min: number;
98
+ max: number;
99
+ }): JsonSchema;
100
+ /** The `items` property of an outline entry, when some pattern on offer has a row of items to vary. */
101
+ export declare function itemsProperty(catalog: Catalog): Record<string, JsonSchema>;
102
+ /** Reads the item count of an outline entry: the pattern's own number when it has no choice or none was given. */
103
+ export declare function readItems(pattern: PatternDef, value: unknown): {
104
+ items?: number;
105
+ problem?: string;
106
+ };