@pipedream/google_slides 0.4.0 → 0.6.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 (29) hide show
  1. package/actions/create-image/create-image.mjs +1 -1
  2. package/actions/create-page-element/create-page-element.mjs +1 -1
  3. package/actions/create-presentation/create-presentation.mjs +1 -1
  4. package/actions/create-slide/create-slide.mjs +1 -1
  5. package/actions/create-table/create-table.mjs +1 -1
  6. package/actions/delete-page-element/delete-page-element.mjs +1 -1
  7. package/actions/delete-slide/delete-slide.mjs +1 -1
  8. package/actions/delete-table-column/delete-table-column.mjs +1 -1
  9. package/actions/delete-table-row/delete-table-row.mjs +1 -1
  10. package/actions/find-presentation/find-presentation.mjs +1 -1
  11. package/actions/format-paragraph/format-paragraph.mjs +236 -0
  12. package/actions/format-shape/format-shape.mjs +193 -0
  13. package/actions/format-slide-background/format-slide-background.mjs +74 -0
  14. package/actions/format-table-cell/format-table-cell.mjs +162 -0
  15. package/actions/format-text/format-text.mjs +248 -0
  16. package/actions/get-presentation/get-presentation.mjs +34 -0
  17. package/actions/insert-table-columns/insert-table-columns.mjs +1 -1
  18. package/actions/insert-table-rows/insert-table-rows.mjs +1 -1
  19. package/actions/insert-text/insert-text.mjs +1 -1
  20. package/actions/insert-text-into-table/insert-text-into-table.mjs +1 -1
  21. package/actions/merge-data/merge-data.mjs +1 -1
  22. package/actions/position-element/position-element.mjs +219 -0
  23. package/actions/refresh-chart/refresh-chart.mjs +1 -1
  24. package/actions/replace-all-text/replace-all-text.mjs +1 -1
  25. package/common/constants.mjs +110 -0
  26. package/common/utils.mjs +115 -0
  27. package/google_slides.app.mjs +124 -1
  28. package/package.json +1 -1
  29. package/sources/new-presentation/new-presentation.mjs +1 -1
@@ -0,0 +1,219 @@
1
+ import { ConfigurationError } from "@pipedream/platform";
2
+ import googleSlides from "../../google_slides.app.mjs";
3
+ import {
4
+ EMU, EMU_PER_POINT, POINTS, Z_ORDER_OPERATIONS,
5
+ } from "../../common/constants.mjs";
6
+
7
+ export default {
8
+ key: "google_slides-position-element",
9
+ name: "Position Element",
10
+ description: "Move, scale, rotate, or restack a page element in a Google Slides presentation. Unspecified properties keep their current values. Use **Get Presentation** to find the element's object ID. [See the documentation](https://developers.google.com/workspace/slides/api/reference/rest/v1/presentations/request#UpdatePageElementTransformRequest)",
11
+ version: "0.0.1",
12
+ annotations: {
13
+ destructiveHint: false,
14
+ openWorldHint: true,
15
+ readOnlyHint: false,
16
+ },
17
+ type: "action",
18
+ props: {
19
+ googleSlides,
20
+ presentationId: {
21
+ propDefinition: [
22
+ googleSlides,
23
+ "staticPresentationId",
24
+ ],
25
+ },
26
+ objectId: {
27
+ propDefinition: [
28
+ googleSlides,
29
+ "pageElementId",
30
+ ],
31
+ },
32
+ translateX: {
33
+ type: "integer",
34
+ label: "X Position",
35
+ description: "Horizontal position in points, measured from the top-left of the slide.",
36
+ optional: true,
37
+ },
38
+ translateY: {
39
+ type: "integer",
40
+ label: "Y Position",
41
+ description: "Vertical position in points, measured from the top-left of the slide.",
42
+ optional: true,
43
+ },
44
+ scaleXPercent: {
45
+ type: "integer",
46
+ label: "Horizontal Scale",
47
+ description: "Horizontal scale as a percentage of the element's intrinsic size (100 = unscaled). Range 1-1000.",
48
+ min: 1,
49
+ max: 1000,
50
+ optional: true,
51
+ },
52
+ scaleYPercent: {
53
+ type: "integer",
54
+ label: "Vertical Scale",
55
+ description: "Vertical scale as a percentage of the element's intrinsic size (100 = unscaled). Range 1-1000.",
56
+ min: 1,
57
+ max: 1000,
58
+ optional: true,
59
+ },
60
+ rotation: {
61
+ type: "integer",
62
+ label: "Rotation",
63
+ description: "Clockwise rotation in degrees (-360 to 360).",
64
+ min: -360,
65
+ max: 360,
66
+ optional: true,
67
+ },
68
+ zOrderOperation: {
69
+ type: "string",
70
+ label: "Z-Order",
71
+ description: "Restack the element relative to the other elements on its slide.",
72
+ options: Z_ORDER_OPERATIONS,
73
+ optional: true,
74
+ },
75
+ },
76
+ async run({ $ }) {
77
+ const {
78
+ googleSlides,
79
+ presentationId,
80
+ objectId,
81
+ translateX,
82
+ translateY,
83
+ scaleXPercent,
84
+ scaleYPercent,
85
+ rotation,
86
+ zOrderOperation,
87
+ } = this;
88
+
89
+ const movesElement = [
90
+ translateX,
91
+ translateY,
92
+ scaleXPercent,
93
+ scaleYPercent,
94
+ rotation,
95
+ ].some((value) => value != null);
96
+
97
+ if (!movesElement && !zOrderOperation) {
98
+ throw new ConfigurationError("Provide at least one of X Position, Y Position, Horizontal Scale, Vertical Scale, Rotation, or Z-Order.");
99
+ }
100
+
101
+ const id = googleSlides.getPresentationId(presentationId);
102
+ const requests = [];
103
+
104
+ const {
105
+ element, groupId,
106
+ } = await googleSlides.getPageElement(id, objectId);
107
+
108
+ // A grouped child's transform is relative to its group, so the slide-space
109
+ // X/Y props would not mean what they say, and the API rejects grouped IDs
110
+ // for z-order outright.
111
+ if (groupId) {
112
+ throw new ConfigurationError(`Page element "${objectId}" is inside group "${groupId}". Its position is relative to that group, and z-order cannot target a grouped element. Target the group itself, or ungroup it first.`);
113
+ }
114
+
115
+ if (movesElement) {
116
+ const current = element.transform || {};
117
+ const unit = current.unit || POINTS;
118
+ const toUnit = (points) => (unit === EMU
119
+ ? points * EMU_PER_POINT
120
+ : points);
121
+
122
+ // A move alone leaves the matrix untouched. When scale or rotation is
123
+ // set, the matrix is decomposed so that reflection (carried by the signed
124
+ // scaleY) and shear survive the rebuild.
125
+ let matrix = {
126
+ scaleX: current.scaleX ?? 1,
127
+ scaleY: current.scaleY ?? 1,
128
+ shearX: current.shearX ?? 0,
129
+ shearY: current.shearY ?? 0,
130
+ };
131
+
132
+ if (scaleXPercent != null || scaleYPercent != null || rotation != null) {
133
+ // Factor any reflection onto the axis that already carries it before
134
+ // decomposing, so changing the rotation cannot move a horizontal flip
135
+ // onto the vertical axis and leave the element 180 degrees out.
136
+ const determinant = (matrix.scaleX * matrix.scaleY) - (matrix.shearY * matrix.shearX);
137
+ const flipX = determinant < 0 && matrix.scaleX < 0
138
+ ? -1
139
+ : 1;
140
+ const flipY = determinant < 0 && flipX === 1
141
+ ? -1
142
+ : 1;
143
+
144
+ const columnX = matrix.scaleX * flipX;
145
+ const columnXShear = matrix.shearY * flipX;
146
+ const columnY = matrix.shearX * flipY;
147
+ const columnYScale = matrix.scaleY * flipY;
148
+
149
+ const currentAngle = Math.atan2(columnXShear, columnX);
150
+ const cos = Math.cos(currentAngle);
151
+ const sin = Math.sin(currentAngle);
152
+
153
+ const currentScaleX = Math.hypot(columnX, columnXShear) || 1;
154
+ const shear = (columnY * cos) + (columnYScale * sin);
155
+ const currentScaleY = (columnYScale * cos) - (columnY * sin);
156
+
157
+ const scaleX = scaleXPercent != null
158
+ ? scaleXPercent / 100
159
+ : currentScaleX;
160
+ const scaleY = scaleYPercent != null
161
+ ? (scaleYPercent / 100) * (currentScaleY < 0
162
+ ? -1
163
+ : 1)
164
+ : currentScaleY;
165
+ const angle = rotation != null
166
+ ? rotation * Math.PI / 180
167
+ : currentAngle;
168
+
169
+ const newCos = Math.cos(angle);
170
+ const newSin = Math.sin(angle);
171
+
172
+ matrix = {
173
+ scaleX: scaleX * newCos * flipX,
174
+ shearY: scaleX * newSin * flipX,
175
+ shearX: ((shear * newCos) - (scaleY * newSin)) * flipY,
176
+ scaleY: ((shear * newSin) + (scaleY * newCos)) * flipY,
177
+ };
178
+ }
179
+
180
+ requests.push({
181
+ updatePageElementTransform: {
182
+ objectId,
183
+ applyMode: "ABSOLUTE",
184
+ transform: {
185
+ ...matrix,
186
+ translateX: translateX != null
187
+ ? toUnit(translateX)
188
+ : current.translateX ?? 0,
189
+ translateY: translateY != null
190
+ ? toUnit(translateY)
191
+ : current.translateY ?? 0,
192
+ unit,
193
+ },
194
+ },
195
+ });
196
+ }
197
+
198
+ if (zOrderOperation) {
199
+ requests.push({
200
+ updatePageElementsZOrder: {
201
+ pageElementObjectIds: [
202
+ objectId,
203
+ ],
204
+ operation: zOrderOperation,
205
+ },
206
+ });
207
+ }
208
+
209
+ await googleSlides.batchUpdate(id, requests);
210
+
211
+ $.export("$summary", `Repositioned element ${objectId}`);
212
+
213
+ return {
214
+ presentationId,
215
+ objectId,
216
+ requestsApplied: requests.map((request) => Object.keys(request)[0]),
217
+ };
218
+ },
219
+ };
@@ -4,7 +4,7 @@ export default {
4
4
  key: "google_slides-refresh-chart",
5
5
  name: "Refresh a chart",
6
6
  description: "Refresh a chart from Sheets. [See the docs here](https://developers.google.com/slides/api/samples/elements#refresh_a_chart_from_sheets)",
7
- version: "0.0.7",
7
+ version: "0.0.9",
8
8
  annotations: {
9
9
  destructiveHint: false,
10
10
  openWorldHint: true,
@@ -4,7 +4,7 @@ export default {
4
4
  key: "google_slides-replace-all-text",
5
5
  name: "Replace All Text",
6
6
  description: "Replace all text in a presentation. [See the documentation](https://developers.google.com/workspace/slides/api/reference/rest/v1/presentations/request#ReplaceAllTextRequest)",
7
- version: "0.0.3",
7
+ version: "0.0.5",
8
8
  annotations: {
9
9
  destructiveHint: true,
10
10
  openWorldHint: true,
@@ -141,3 +141,113 @@ export const SHAPE_TYPES = [
141
141
  "ELLIPSE_RIBBON_2",
142
142
  "CLOUD_CALLOUT",
143
143
  ];
144
+
145
+ // Enum values used by the styling requests, checked against the current API
146
+ // reference. https://developers.google.com/workspace/slides/api/reference/rest/v1/presentations/request
147
+
148
+ export const ALIGNMENTS = [
149
+ "START",
150
+ "CENTER",
151
+ "END",
152
+ "JUSTIFIED",
153
+ ];
154
+
155
+ export const CONTENT_ALIGNMENTS = [
156
+ "TOP",
157
+ "MIDDLE",
158
+ "BOTTOM",
159
+ ];
160
+
161
+ export const DASH_STYLES = [
162
+ "SOLID",
163
+ "DOT",
164
+ "DASH",
165
+ "DASH_DOT",
166
+ "LONG_DASH",
167
+ "LONG_DASH_DOT",
168
+ ];
169
+
170
+ export const Z_ORDER_OPERATIONS = [
171
+ "BRING_TO_FRONT",
172
+ "BRING_FORWARD",
173
+ "SEND_BACKWARD",
174
+ "SEND_TO_BACK",
175
+ ];
176
+
177
+ // Theme colors resolve against the deck's own ColorScheme, so a caller can style
178
+ // with the presentation's palette instead of hardcoding hex values.
179
+ export const THEME_COLORS = [
180
+ "DARK1",
181
+ "LIGHT1",
182
+ "DARK2",
183
+ "LIGHT2",
184
+ "ACCENT1",
185
+ "ACCENT2",
186
+ "ACCENT3",
187
+ "ACCENT4",
188
+ "ACCENT5",
189
+ "ACCENT6",
190
+ "HYPERLINK",
191
+ "FOLLOWED_HYPERLINK",
192
+ "TEXT1",
193
+ "BACKGROUND1",
194
+ "TEXT2",
195
+ "BACKGROUND2",
196
+ ];
197
+
198
+ // Dimension.unit - points, rather than the API's other unit (EMU).
199
+ export const POINTS = "PT";
200
+ export const EMU = "EMU";
201
+
202
+ // 1 inch = 72 pt = 914400 EMU. A transform carries a single unit for all six
203
+ // matrix elements, so caller-supplied points must be converted into whatever
204
+ // unit the element already uses rather than mixed in.
205
+ export const EMU_PER_POINT = 12700;
206
+
207
+ // Range.type. The API expresses the three range shapes as distinct types rather
208
+ // than as optional bounds, and rejects FIXED_RANGE that carries no end index.
209
+ export const TEXT_RANGE_ALL = "ALL";
210
+ export const TEXT_RANGE_FROM_START_INDEX = "FROM_START_INDEX";
211
+ export const TEXT_RANGE_FIXED_RANGE = "FIXED_RANGE";
212
+
213
+ // BulletGlyphPreset. NOTE: these are NOT the same spellings the Google Docs API
214
+ // uses - Slides says DIGIT where Docs says DECIMAL - so they cannot be shared
215
+ // with the google_docs connector.
216
+ export const BULLET_PRESETS = [
217
+ "BULLET_DISC_CIRCLE_SQUARE",
218
+ "BULLET_DIAMONDX_ARROW3D_SQUARE",
219
+ "BULLET_CHECKBOX",
220
+ "BULLET_ARROW_DIAMOND_DISC",
221
+ "BULLET_STAR_CIRCLE_SQUARE",
222
+ "BULLET_ARROW3D_CIRCLE_SQUARE",
223
+ "BULLET_LEFTTRIANGLE_DIAMOND_DISC",
224
+ "BULLET_DIAMONDX_HOLLOWDIAMOND_SQUARE",
225
+ "BULLET_DIAMOND_CIRCLE_SQUARE",
226
+ "NUMBERED_DIGIT_ALPHA_ROMAN",
227
+ "NUMBERED_DIGIT_ALPHA_ROMAN_PARENS",
228
+ "NUMBERED_DIGIT_NESTED",
229
+ "NUMBERED_UPPERALPHA_ALPHA_ROMAN",
230
+ "NUMBERED_UPPERROMAN_UPPERALPHA_DIGIT",
231
+ "NUMBERED_ZERODIGIT_ALPHA_ROMAN",
232
+ ];
233
+
234
+ // Sentinel for the Bullets prop meaning "strip existing bullets".
235
+ export const BULLETS_NONE = "NONE";
236
+
237
+ export const BASELINE_OFFSETS = [
238
+ "NONE",
239
+ "SUPERSCRIPT",
240
+ "SUBSCRIPT",
241
+ ];
242
+
243
+ export const TEXT_DIRECTIONS = [
244
+ "LEFT_TO_RIGHT",
245
+ "RIGHT_TO_LEFT",
246
+ ];
247
+
248
+ export const SPACING_MODES = [
249
+ "NEVER_COLLAPSE",
250
+ "COLLAPSE_LISTS",
251
+ ];
252
+
253
+ export const OPACITY_SCALE = 100;
@@ -0,0 +1,115 @@
1
+ import { ConfigurationError } from "@pipedream/platform";
2
+ import {
3
+ TEXT_RANGE_ALL,
4
+ TEXT_RANGE_FIXED_RANGE,
5
+ OPACITY_SCALE,
6
+ POINTS,
7
+ TEXT_RANGE_FROM_START_INDEX,
8
+ THEME_COLORS,
9
+ } from "./constants.mjs";
10
+
11
+ function toOpaqueColor(value) {
12
+ const trimmed = String(value).trim();
13
+ const themeColor = trimmed.toUpperCase();
14
+ if (THEME_COLORS.includes(themeColor)) {
15
+ return {
16
+ themeColor,
17
+ };
18
+ }
19
+
20
+ const hex = trimmed.replace(/^#/, "");
21
+ if (!/^[0-9a-fA-F]{6}$/.test(hex)) {
22
+ return null;
23
+ }
24
+ const channel = (start) => parseInt(hex.slice(start, start + 2), 16) / 255;
25
+ return {
26
+ rgbColor: {
27
+ red: channel(0),
28
+ green: channel(2),
29
+ blue: channel(4),
30
+ },
31
+ };
32
+ }
33
+
34
+ function toSolidFill(value, opacityPercent) {
35
+ const color = value == null || value === ""
36
+ ? null
37
+ : toOpaqueColor(value);
38
+ if (value && !color) {
39
+ return null;
40
+ }
41
+ const fill = {};
42
+ if (color) {
43
+ fill.color = color;
44
+ }
45
+ if (opacityPercent != null) {
46
+ fill.alpha = opacityPercent / OPACITY_SCALE;
47
+ }
48
+ return Object.keys(fill).length
49
+ ? fill
50
+ : null;
51
+ }
52
+
53
+ function buildTextRange(startIndex, endIndex) {
54
+ if (startIndex == null && endIndex == null) {
55
+ return {
56
+ type: TEXT_RANGE_ALL,
57
+ };
58
+ }
59
+ if (startIndex == null) {
60
+ throw new ConfigurationError("End Index needs a Start Index. Provide both to style a fixed span, Start Index on its own to style from there to the end, or neither to style all of the text.");
61
+ }
62
+ if (endIndex == null) {
63
+ return {
64
+ type: TEXT_RANGE_FROM_START_INDEX,
65
+ startIndex,
66
+ };
67
+ }
68
+ if (endIndex <= startIndex) {
69
+ throw new ConfigurationError(`End Index (${endIndex}) must be greater than Start Index (${startIndex}).`);
70
+ }
71
+ return {
72
+ type: TEXT_RANGE_FIXED_RANGE,
73
+ startIndex,
74
+ endIndex,
75
+ };
76
+ }
77
+
78
+ function styleBuilder() {
79
+ const style = {};
80
+ const fields = [];
81
+
82
+ return {
83
+ style,
84
+ fields,
85
+ set(name, value) {
86
+ if (value == null) {
87
+ return;
88
+ }
89
+ style[name] = value;
90
+ fields.push(name);
91
+ },
92
+ setDimension(name, magnitude, unit = POINTS) {
93
+ if (magnitude == null) {
94
+ return;
95
+ }
96
+ this.set(name, {
97
+ magnitude,
98
+ unit,
99
+ });
100
+ },
101
+ get isEmpty() {
102
+ return !fields.length;
103
+ },
104
+ get mask() {
105
+ return fields.join(",");
106
+ },
107
+ };
108
+ }
109
+
110
+ export default {
111
+ styleBuilder,
112
+ toOpaqueColor,
113
+ toSolidFill,
114
+ buildTextRange,
115
+ };
@@ -1,5 +1,7 @@
1
+ import { ConfigurationError } from "@pipedream/platform";
1
2
  import slides from "@googleapis/slides";
2
3
  import googleDrive from "@pipedream/google_drive";
4
+ import { CONTENT_ALIGNMENTS } from "./common/constants.mjs";
3
5
 
4
6
  export default {
5
7
  ...googleDrive,
@@ -19,6 +21,72 @@ export default {
19
21
  return this.listPresentationsOptions(driveId, nextPageToken);
20
22
  },
21
23
  },
24
+ staticPresentationId: {
25
+ type: "string",
26
+ label: "Presentation ID",
27
+ description: "The ID of the presentation. This is the long string in the URL: `https://docs.google.com/presentation/d/{PRESENTATION_ID}/edit`. A full presentation URL is also accepted. Use **Find Presentation** to resolve a name to its ID.",
28
+ },
29
+ staticSlideId: {
30
+ type: "string",
31
+ label: "Slide ID",
32
+ description: "The object ID of the slide (e.g. `p1` or `g1a2b3c4d5`). Use **Get Presentation** and read `slides[].objectId`.",
33
+ },
34
+ pageElementId: {
35
+ type: "string",
36
+ label: "Page Element ID",
37
+ description: "The object ID of the shape, image, table, or other page element to act on. Use **Get Presentation** and read `slides[].pageElements[].objectId`.",
38
+ },
39
+ // Shared by the styling actions; individual actions override the label or
40
+ // description where their wording differs.
41
+ rowIndex: {
42
+ type: "integer",
43
+ label: "Row Index",
44
+ description: "The 0-based row of the target table cell.",
45
+ min: 0,
46
+ optional: true,
47
+ },
48
+ columnIndex: {
49
+ type: "integer",
50
+ label: "Column Index",
51
+ description: "The 0-based column of the target table cell.",
52
+ min: 0,
53
+ optional: true,
54
+ },
55
+ startIndex: {
56
+ type: "integer",
57
+ label: "Start Index",
58
+ description: "Character index to style from, inclusive. On its own, styles from here to the end of the text. Omit both indices to style all of it.",
59
+ min: 0,
60
+ optional: true,
61
+ },
62
+ endIndex: {
63
+ type: "integer",
64
+ label: "End Index",
65
+ description: "Character index to style up to, exclusive. Requires **Start Index**, and must be greater than it.",
66
+ min: 0,
67
+ optional: true,
68
+ },
69
+ backgroundColor: {
70
+ type: "string",
71
+ label: "Background Color",
72
+ description: "A hex code (e.g. `#EEEEEE`) or one of the deck's theme colors (e.g. `ACCENT1`).",
73
+ optional: true,
74
+ },
75
+ backgroundOpacity: {
76
+ type: "integer",
77
+ label: "Background Opacity",
78
+ description: "Opacity as a whole percentage, from `0` (fully transparent) to `100` (fully opaque). Can be set on its own to change an existing fill's opacity without restating its color.",
79
+ min: 0,
80
+ max: 100,
81
+ optional: true,
82
+ },
83
+ contentAlignment: {
84
+ type: "string",
85
+ label: "Content Alignment",
86
+ description: "Vertical alignment of the text within the element. One of `TOP`, `MIDDLE` or `BOTTOM` - e.g. `MIDDLE` to centre the text vertically.",
87
+ options: CONTENT_ALIGNMENTS,
88
+ optional: true,
89
+ },
22
90
  layoutId: {
23
91
  type: "string",
24
92
  label: "Layout ID",
@@ -251,13 +319,68 @@ export default {
251
319
  }
252
320
  return this.listFilesOptions(pageToken, request);
253
321
  },
254
- async getPresentation(presentationId) {
322
+ getPresentationId(idOrUrl) {
323
+ const input = String(idOrUrl).trim();
324
+ if (!input) {
325
+ throw new ConfigurationError("Presentation ID is required.");
326
+ }
327
+ // Published presentations use /presentation/d/e/{token}/pub and have no
328
+ // editable ID that presentations.get accepts, so reject them explicitly
329
+ // rather than extracting the literal "e" segment as the ID.
330
+ if (/\/presentation\/d\/e\//.test(input)) {
331
+ throw new ConfigurationError("Published presentation URLs (`/presentation/d/e/...`) are not supported. Provide the presentation's ID or its editable URL (`https://docs.google.com/presentation/d/{ID}/edit`).");
332
+ }
333
+ // Accept a plain presentation ID or a full Slides URL
334
+ // (e.g. https://docs.google.com/presentation/d/{ID}/edit).
335
+ const match = input.match(/\/presentation\/d\/([a-zA-Z0-9-_]+)/);
336
+ return match
337
+ ? match[1]
338
+ : input;
339
+ },
340
+ async getPresentation(presentationId, fields) {
255
341
  const slides = this.slides();
256
342
  const request = {
257
343
  presentationId,
344
+ ...fields && {
345
+ fields,
346
+ },
258
347
  };
259
348
  return (await slides.presentations.get(request)).data;
260
349
  },
350
+ _findPageElement(presentation, objectId) {
351
+ const walk = (elements, slideId, groupId) => {
352
+ for (const element of elements || []) {
353
+ if (element.objectId === objectId) {
354
+ return {
355
+ element,
356
+ slideId,
357
+ groupId,
358
+ };
359
+ }
360
+ const found = walk(element.elementGroup?.children, slideId, element.objectId);
361
+ if (found) {
362
+ return found;
363
+ }
364
+ }
365
+ return null;
366
+ };
367
+
368
+ for (const slide of presentation.slides || []) {
369
+ const found = walk(slide.pageElements, slide.objectId);
370
+ if (found) {
371
+ return found;
372
+ }
373
+ }
374
+ return null;
375
+ },
376
+ async getPageElement(presentationId, objectId) {
377
+ const presentation = await this.getPresentation(presentationId);
378
+ const found = this._findPageElement(presentation, objectId);
379
+ if (!found) {
380
+ throw new ConfigurationError(`No page element with ID "${objectId}" was found in presentation ${presentationId}. Use Get Presentation to list slides[].pageElements[].objectId.`);
381
+ }
382
+ return found;
383
+ },
261
384
  async getSlide(presentationId, slideId) {
262
385
  const slides = this.slides();
263
386
  const request = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipedream/google_slides",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Pipedream Google Slides Components",
5
5
  "main": "google_slides.app.mjs",
6
6
  "keywords": [
@@ -7,7 +7,7 @@ export default {
7
7
  type: "source",
8
8
  name: "New Presentation (Instant)",
9
9
  description: "Emit new event each time a new presentation is created in a drive.",
10
- version: "0.0.5",
10
+ version: "0.0.7",
11
11
  hooks: {
12
12
  ...newFilesInstant.hooks,
13
13
  async deploy() {