Skip to main content
Glama
slides.ts7.29 kB
import { GoogleSlidesAuth } from './auth.js'; export interface SlideInfo { slideId: string; title?: string; } export interface RectangleOptions { slideId: string; x?: number; y?: number; width?: number; height?: number; } export class GoogleSlidesService { private auth: GoogleSlidesAuth; constructor(auth: GoogleSlidesAuth) { this.auth = auth; } /** * Create a new slide in the presentation */ async createSlide(presentationId: string, insertionIndex?: number): Promise<SlideInfo> { await this.auth.refreshTokenIfNeeded(); const slides = this.auth.getSlidesClient(); const requests = [{ createSlide: { objectId: `slide_${Date.now()}`, insertionIndex: insertionIndex || 0, slideLayoutReference: { predefinedLayout: 'BLANK' } } }]; try { const response = await slides.presentations.batchUpdate({ presentationId, requestBody: { requests } }); const createdSlide = response.data.replies[0].createSlide; return { slideId: createdSlide.objectId, title: 'New Slide' }; } catch (error) { console.error('Error creating slide:', error); throw new Error(`Failed to create slide: ${error}`); } } /** * Add a rectangle to a slide with 40% of slide dimensions */ async addRectangle(presentationId: string, options: RectangleOptions): Promise<string> { await this.auth.refreshTokenIfNeeded(); const slides = this.auth.getSlidesClient(); // Get presentation details to calculate slide dimensions const presentation = await slides.presentations.get({ presentationId }); // Verify the slide exists const slideExists = presentation.data.slides?.find((slide: any) => slide.objectId === options.slideId); if (!slideExists) { console.error(`Available slides:`, presentation.data.slides?.map((s: any) => s.objectId)); throw new Error(`Slide with ID ${options.slideId} not found in presentation`); } console.error(`Confirmed slide ${options.slideId} exists in presentation`); const pageSize = presentation.data.pageSize; const slideWidth = pageSize.width.magnitude; const slideHeight = pageSize.height.magnitude; const slideUnit = pageSize.width.unit; // Usually 'EMU' console.error(`Page size unit: ${slideUnit}`); // Convert from EMU to PT if needed (1 EMU = 1/914400 inches, 1 PT = 1/72 inches) // So 1 PT = 12700 EMU let slideWidthPt, slideHeightPt; if (slideUnit === 'EMU') { slideWidthPt = slideWidth / 12700; slideHeightPt = slideHeight / 12700; } else { slideWidthPt = slideWidth; slideHeightPt = slideHeight; } // Calculate rectangle dimensions (40% of slide size for better visibility) const rectWidth = slideWidthPt * 0.4; const rectHeight = slideHeightPt * 0.4; // Position rectangle in center if no position specified const x = options.x || (slideWidthPt - rectWidth) / 2; const y = options.y || (slideHeightPt - rectHeight) / 2; const rectangleId = `rectangle_${Date.now()}`; console.error(`Creating RED rectangle with dimensions: ${rectWidth} x ${rectHeight} PT at position (${x}, ${y}) PT`); console.error(`Original slide dimensions: ${slideWidth} x ${slideHeight} ${slideUnit}`); console.error(`Converted slide dimensions: ${slideWidthPt} x ${slideHeightPt} PT`); console.error(`Target slide ID: ${options.slideId}`); console.error(`Rectangle ID: ${rectangleId}`); const requests = [{ createShape: { objectId: rectangleId, shapeType: 'RECTANGLE', elementProperties: { pageObjectId: options.slideId, size: { width: { magnitude: options.width || rectWidth, unit: 'PT' }, height: { magnitude: options.height || rectHeight, unit: 'PT' } }, transform: { scaleX: 1, scaleY: 1, translateX: x, translateY: y, unit: 'PT' } } } }]; try { console.error('Sending rectangle creation request:', JSON.stringify(requests, null, 2)); // First create the shape await slides.presentations.batchUpdate({ presentationId, requestBody: { requests } }); // Then update its styling with bright red color const styleRequests = [{ updateShapeProperties: { objectId: rectangleId, shapeProperties: { shapeBackgroundFill: { solidFill: { color: { rgbColor: { red: 1.0, green: 0.0, blue: 0.0 } } } }, outline: { outlineFill: { solidFill: { color: { rgbColor: { red: 0.0, green: 0.0, blue: 0.0 } } } }, weight: { magnitude: 4, unit: 'PT' } } }, fields: 'shapeBackgroundFill,outline' } }]; console.error('Sending shape styling request:', JSON.stringify(styleRequests, null, 2)); const styleResponse = await slides.presentations.batchUpdate({ presentationId, requestBody: { requests: styleRequests } }); console.error('Shape styling response:', JSON.stringify(styleResponse.data, null, 2)); return rectangleId; } catch (error) { console.error('Error adding rectangle:', error); console.error('Full error details:', JSON.stringify(error, null, 2)); throw new Error(`Failed to add rectangle: ${error}`); } } /** * Get presentation information */ async getPresentationInfo(presentationId: string) { await this.auth.refreshTokenIfNeeded(); const slides = this.auth.getSlidesClient(); try { const response = await slides.presentations.get({ presentationId }); return { title: response.data.title, slideCount: response.data.slides?.length || 0, pageSize: response.data.pageSize }; } catch (error) { console.error('Error getting presentation info:', error); throw new Error(`Failed to get presentation info: ${error}`); } } /** * List all slides in a presentation */ async listSlides(presentationId: string): Promise<SlideInfo[]> { await this.auth.refreshTokenIfNeeded(); const slides = this.auth.getSlidesClient(); try { const response = await slides.presentations.get({ presentationId }); return response.data.slides?.map((slide: any, index: number) => ({ slideId: slide.objectId, title: `Slide ${index + 1}` })) || []; } catch (error) { console.error('Error listing slides:', error); throw new Error(`Failed to list slides: ${error}`); } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/vamsikiran353-gif/google-slides-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server