Spaces:
Sleeping
Sleeping
File size: 4,748 Bytes
45b3fab 1eb0838 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 | /*
* generated-presentation.ts
* Purpose: Defines the typed shape of generated presentation responses and helpers for normalizing them into SlideSpec data.
* Used by: GoogleSlidesEditor and generation API consumers.
* Depends on: SlideSpec types and slide-prompt layout normalization.
*/
import type { SlideSpec } from '@/data/templates';
import { normalizeLayout } from '@/lib/slide-prompt';
export interface PresentationApiColumn {
heading?: string;
text?: string;
}
export interface PresentationApiSlide {
id?: string;
title?: string;
subtitle?: string;
content?: string | string[];
columns?: PresentationApiColumn[];
imageUrl?: string;
imageKeyword?: string;
layout?: string;
}
export interface PresentationGenerateSuccessResponse {
presentationName?: string;
slides: PresentationApiSlide[];
generationSource: 'model';
}
export interface PresentationGenerateErrorResponse {
error: string;
details?: string;
}
function toStringArray(value: unknown): string[] {
if (Array.isArray(value)) {
return value
.filter((entry): entry is string => typeof entry === 'string')
.map((entry) => entry.trim())
.filter(Boolean);
}
if (typeof value === 'string' && value.trim()) {
return [value.trim()];
}
return [];
}
function normalizeColumns(
value: unknown,
content: unknown
): Array<{ heading: string; text: string }> | undefined {
if (Array.isArray(value) && value.length > 0) {
const columns = value
.map((entry, index) => {
if (!entry || typeof entry !== 'object') return null;
const headingValue = (entry as { heading?: unknown }).heading;
const textValue = (entry as { text?: unknown }).text;
const heading = typeof headingValue === 'string' && headingValue.trim()
? headingValue.trim()
: `Column ${index + 1}`;
const text = typeof textValue === 'string' ? textValue.trim() : '';
if (!heading && !text) return null;
return { heading, text };
})
.filter((entry): entry is { heading: string; text: string } => Boolean(entry))
.slice(0, 3);
if (columns.length > 0) {
return columns;
}
}
const contentItems = toStringArray(content).slice(0, 3);
if (!contentItems.length) return undefined;
return contentItems.map((text, index) => ({
heading: `Pillar ${index + 1}`,
text,
}));
}
export function getPresentationSlideContentLines(content: unknown): string[] {
return toStringArray(content);
}
export function mapPresentationSlidesToSlideSpecs(
slides: PresentationApiSlide[],
templateId: string
): SlideSpec[] {
const totalSlides = slides.length;
return slides.map((slide, index) => {
const layout = normalizeLayout(typeof slide.layout === 'string' ? slide.layout : '', index, totalSlides);
const contentLines = getPresentationSlideContentLines(slide.content);
const subtitle = typeof slide.subtitle === 'string' && slide.subtitle.trim()
? slide.subtitle.trim()
: ((layout === 'title_subtitle' || layout === 'thank_you') ? contentLines[0] : undefined);
return {
id: typeof slide.id === 'string' && slide.id.trim() ? slide.id : `slide-${index + 1}`,
templateId,
layout,
title: typeof slide.title === 'string' && slide.title.trim() ? slide.title.trim() : `Slide ${index + 1}`,
subtitle,
body: layout === 'title_and_text' || layout === 'image_and_text'
? (contentLines.length ? [{ text: contentLines.join(' ') }] : undefined)
: undefined,
items: layout === 'agenda' || layout === 'references'
? contentLines.map((text) => ({ text }))
: undefined,
columns: layout === 'three_columns'
? normalizeColumns(slide.columns, slide.content)
: undefined,
imageUrl: typeof slide.imageUrl === 'string' && slide.imageUrl.trim() ? slide.imageUrl : undefined,
};
});
}
export function isPresentationGenerateSuccessResponse(
value: unknown
): value is PresentationGenerateSuccessResponse {
if (!value || typeof value !== 'object') return false;
const candidate = value as Partial<PresentationGenerateSuccessResponse>;
return candidate.generationSource === 'model' && Array.isArray(candidate.slides);
}
export function getPresentationGenerateErrorMessage(value: unknown): string {
if (!value || typeof value !== 'object') {
return 'Failed to generate slides.';
}
const candidate = value as PresentationGenerateErrorResponse;
if (typeof candidate.details === 'string' && candidate.details.trim()) {
return candidate.details;
}
if (typeof candidate.error === 'string' && candidate.error.trim()) {
return candidate.error;
}
return 'Failed to generate slides.';
}
|