@mcpher/gas-fakes 2.5.2 → 2.5.3

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 (50) hide show
  1. package/README.md +8 -1
  2. package/package.json +1 -1
  3. package/pngs/srv.jpg +0 -0
  4. package/src/cli/app.js +1 -0
  5. package/src/cli/togas.js +23 -14
  6. package/src/index.js +1 -0
  7. package/src/services/advslides/fakeadvslides.js +11 -5
  8. package/src/services/base/app.js +9 -0
  9. package/src/services/base/fakebase.js +28 -0
  10. package/src/services/common/fakeadvresource.js +3 -2
  11. package/src/services/enums/baseenums.js +47 -0
  12. package/src/services/enums/slidesenums.js +3 -1
  13. package/src/services/html/serverworker.js +1 -1
  14. package/src/services/slidesapp/app.js +5 -0
  15. package/src/services/slidesapp/fakeautofit.js +1 -1
  16. package/src/services/slidesapp/fakeborder.js +106 -0
  17. package/src/services/slidesapp/fakecolorscheme.js +1 -1
  18. package/src/services/slidesapp/fakefill.js +216 -0
  19. package/src/services/slidesapp/fakegroup.js +35 -0
  20. package/src/services/slidesapp/fakeimage.js +118 -0
  21. package/src/services/slidesapp/fakelayout.js +351 -0
  22. package/src/services/slidesapp/fakeline.js +2 -2
  23. package/src/services/slidesapp/fakelinefill.js +15 -16
  24. package/src/services/slidesapp/fakelink.js +20 -3
  25. package/src/services/slidesapp/fakelist.js +36 -0
  26. package/src/services/slidesapp/fakeliststyle.js +105 -0
  27. package/src/services/slidesapp/fakemaster.js +358 -0
  28. package/src/services/slidesapp/fakenotesmaster.js +125 -0
  29. package/src/services/slidesapp/fakenotespage.js +102 -2
  30. package/src/services/slidesapp/fakepagebackground.js +109 -1
  31. package/src/services/slidesapp/fakepageelement.js +157 -18
  32. package/src/services/slidesapp/fakepageelementrange.js +28 -0
  33. package/src/services/slidesapp/fakepagerange.js +28 -0
  34. package/src/services/slidesapp/fakeparagraphstyle.js +139 -0
  35. package/src/services/slidesapp/fakepicturefill.js +32 -0
  36. package/src/services/slidesapp/fakepresentation.js +126 -2
  37. package/src/services/slidesapp/fakeshape.js +9 -0
  38. package/src/services/slidesapp/fakeslide.js +216 -24
  39. package/src/services/slidesapp/fakesolidfill.js +45 -0
  40. package/src/services/slidesapp/fakespeakerspotlight.js +18 -0
  41. package/src/services/slidesapp/faketable.js +55 -9
  42. package/src/services/slidesapp/faketablecell.js +141 -12
  43. package/src/services/slidesapp/faketablecellrange.js +28 -0
  44. package/src/services/slidesapp/faketablecolumn.js +72 -0
  45. package/src/services/slidesapp/faketablerow.js +31 -0
  46. package/src/services/slidesapp/faketextrange.js +179 -135
  47. package/src/services/slidesapp/faketextstyle.js +158 -0
  48. package/src/services/slidesapp/fakevideo.js +35 -0
  49. package/src/services/slidesapp/fakewordart.js +22 -0
  50. package/src/services/slidesapp/pageelementfactory.js +24 -1
package/README.md CHANGED
@@ -167,17 +167,24 @@ As I mentioned earlier, to take this further, I'm going to need a lot of help to
167
167
 
168
168
  [![Use natural language with gf_agent](./pngs/gfagent.png)](https://youtu.be/lujByoX71HU)
169
169
 
170
+ ## Watch the local webapps and addons development video
171
+
172
+ [![Local Apps Script Webapp and UI Emulation with gas-fakes](./pngs/srv.jpg)](https://youtu.be/vH9wl7QloZ4)
173
+
170
174
  ## Read more docs
171
175
 
172
176
  - [release notes](versionnotes/)
173
177
  - [gas fakes intro video](https://youtu.be/oEjpIrkYpEM)
174
178
  - [getting started](GETTING_STARTED.md) - how to handle authentication for Workspace scopes.
175
179
  - [readme](README.md)
180
+ - [apps script parity](notes/parity.md)
176
181
  - [Natural Language Automation with Gemini Skills & MCP Server](notes/gemini-skills-mcp.md) - new skills-based agent approach.
177
182
  - [Add agent skills to gf_agent](https://ramblings.mcpher.com/add-skills-gf_agent/)
178
183
  - [gf_agent documentation](../gf_agent/README.md) - instructions for the Gemini CLI automation agent and MCP server.
179
184
  - [gas fakes cli](notes/gas-fakes-cli.md)
180
- -[local add-on and webapp development with gas-fakes](notes/local-web-development.md)
185
+ - [local add-on and webapp development with gas-fakes](notes/local-web-development.md)
186
+ - [Bringing the webapp home](https://ramblings.mcpher.com/local-apps-script-webapp-and-ui-emulation/)
187
+ - [Local development example code](https://github.com/brucemcpherson/gf-serve)
181
188
  - [github actions using adc](https://github.com/brucemcpherson/gas-fakes-actions-adc)
182
189
  - [github actions using dwd and wif](https://github.com/brucemcpherson/gas-fakes-actions-dwd)
183
190
  - [ksuite as a back end](notes/ksuite_poc.md)
package/package.json CHANGED
@@ -39,7 +39,7 @@
39
39
  },
40
40
  "name": "@mcpher/gas-fakes",
41
41
  "author": "bruce mcpherson",
42
- "version": "2.5.2",
42
+ "version": "2.5.3",
43
43
  "license": "MIT",
44
44
  "main": "main.js",
45
45
  "description": "An implementation of the Google Workspace Apps Script runtime: Run native App Script Code on Node and Cloud Run",
package/pngs/srv.jpg ADDED
Binary file
package/src/cli/app.js CHANGED
@@ -219,6 +219,7 @@ export async function main() {
219
219
  .option("-s, --source <string>", "Source directory (default: ./ )")
220
220
  .option("-e, --env <path>", "Path to a custom .env file.", "./.env")
221
221
  .option("--scriptId <string>", "Script ID for the target clasp project.")
222
+ .option("-q, --quiet", "Automatically accept all prompts.")
222
223
  .action(togas);
223
224
 
224
225
  program.showHelpAfterError("(add --help for additional information)");
package/src/cli/togas.js CHANGED
@@ -20,6 +20,7 @@ export async function togas(options) {
20
20
  const scriptId = options.scriptId || process.env.TOGAS_SCRIPT_ID || process.env.GF_SCRIPT_ID;
21
21
  const pattern = options.pattern || process.env.TOGAS_PATTERN || "*";
22
22
  const source = options.source || "./";
23
+ const autoAccept = !!options.quiet;
23
24
 
24
25
  if (!target) {
25
26
  console.error("Error: TOGAS_TARGET is not set. Please run 'gas-fakes init' or provide --target.");
@@ -41,14 +42,18 @@ export async function togas(options) {
41
42
 
42
43
  const claspJsonPath = path.join(absoluteTarget, ".clasp.json");
43
44
  if (!fs.existsSync(claspJsonPath)) {
44
- const response = await prompts({
45
- type: "confirm",
46
- name: "create",
47
- message: `No .clasp.json found in ${target}. Create one?`,
48
- initial: true
49
- });
45
+ let create = autoAccept;
46
+ if (!create) {
47
+ const response = await prompts({
48
+ type: "confirm",
49
+ name: "create",
50
+ message: `No .clasp.json found in ${target}. Create one?`,
51
+ initial: true
52
+ });
53
+ create = response.create;
54
+ }
50
55
 
51
- if (response.create) {
56
+ if (create) {
52
57
  if (!scriptId) {
53
58
  console.error("Error: No Script ID found. Please provide one with --scriptId or in .env.");
54
59
  process.exit(1);
@@ -157,14 +162,18 @@ export async function togas(options) {
157
162
  console.log(`Ready for clasp push in ${target}.`);
158
163
 
159
164
  // 5. Clasp push
160
- const response = await prompts({
161
- type: "confirm",
162
- name: "push",
163
- message: "Push to Apps Script using clasp now?",
164
- initial: true
165
- });
165
+ let push = autoAccept;
166
+ if (!push) {
167
+ const response = await prompts({
168
+ type: "confirm",
169
+ name: "push",
170
+ message: "Push to Apps Script using clasp now?",
171
+ initial: true
172
+ });
173
+ push = response.push;
174
+ }
166
175
 
167
- if (response.push) {
176
+ if (push) {
168
177
  console.log("Running clasp push...");
169
178
  try {
170
179
  execSync("clasp push", { cwd: absoluteTarget, stdio: "inherit" });
package/src/index.js CHANGED
@@ -11,6 +11,7 @@ import './services/gmailapp/app.js'
11
11
  import './services/calendarapp/app.js'
12
12
  import './services/chartsapp/app.js'
13
13
  import './services/session/app.js'
14
+ import './services/base/app.js'
14
15
  import './services/advdrive/app.js'
15
16
  import './services/advsheets/app.js'
16
17
  import './services/advdocs/app.js'
@@ -16,15 +16,15 @@ class FakeAdvSlidesPresentations extends FakeAdvResource {
16
16
  // Override 'get' to use the caching-enabled function fxSlidesGet.
17
17
  get(presentationId, options) {
18
18
  ScriptApp.__behavior.isAccessible(presentationId, 'Slides', 'read');
19
- const { response, data } = this._call('get', { presentationId, ...options }, Syncit.fxSlidesGet);
19
+ const { response, data } = this._call('get', { presentationId, ...options }, {}, null, Syncit.fxSlidesGet);
20
20
  gError(response, 'slides.presentations', 'get');
21
21
  return data;
22
22
  }
23
23
 
24
24
  // Signature matches Apps Script advanced service.
25
25
  create(presentation) {
26
- // The underlying API wants the resource in a 'resource' property.
27
- const result = this._call('create', { resource: presentation });
26
+ // The underlying API wants the resource in a 'requestBody' property for Node.js.
27
+ const result = this._call('create', { requestBody: presentation });
28
28
  if (result.data) {
29
29
  this.slides.__addAllowed(result.data.presentationId);
30
30
  }
@@ -32,12 +32,18 @@ class FakeAdvSlidesPresentations extends FakeAdvResource {
32
32
  }
33
33
 
34
34
  // Signature matches Apps Script advanced service.
35
- batchUpdate(requests, presentationId) {
35
+ // Robustly handle both resource object and raw requests array.
36
+ batchUpdate(resource, presentationId) {
36
37
  // for slides, any batch update is a write
37
38
  ScriptApp.__behavior.isAccessible(presentationId, 'Slides', 'write');
39
+
40
+ const requestBody = Array.isArray(resource)
41
+ ? { requests: resource }
42
+ : resource;
43
+
38
44
  const result = this._call('batchUpdate', {
39
45
  presentationId,
40
- resource: { requests },
46
+ requestBody,
41
47
  });
42
48
  gError(result.response, 'slides.presentations', 'batchUpdate');
43
49
 
@@ -0,0 +1,9 @@
1
+ /**
2
+ * the idea here is to create an empty global entry for the singleton
3
+ * but only load it when it is actually used.
4
+ */
5
+ import { lazyLoaderApp } from '../common/lazyloader.js'
6
+ import { newFakeBase as maker } from './fakebase.js';
7
+
8
+ let _app = null;
9
+ _app = lazyLoaderApp(_app, 'Base', maker);
@@ -0,0 +1,28 @@
1
+ import { Proxies } from '../../support/proxies.js';
2
+ import * as BaseEnums from '../enums/baseenums.js';
3
+ import { newFakeMimeType } from '../mimetype/fakemimetype.js';
4
+
5
+ /**
6
+ * @class FakeBase
7
+ * @description The Base service in Google Apps Script.
8
+ * @see https://developers.google.com/apps-script/reference/base
9
+ */
10
+ export class FakeBase {
11
+ constructor() {
12
+ this.Button = BaseEnums.Button;
13
+ this.ButtonSet = BaseEnums.ButtonSet;
14
+ this.ColorType = BaseEnums.ColorType;
15
+ this.MimeType = newFakeMimeType();
16
+ this.Month = BaseEnums.Month;
17
+ this.Weekday = BaseEnums.Weekday;
18
+ }
19
+
20
+ toString() {
21
+ return 'Base';
22
+ }
23
+ }
24
+
25
+ /**
26
+ * @returns {FakeBase}
27
+ */
28
+ export const newFakeBase = () => Proxies.guard(new FakeBase());
@@ -14,7 +14,7 @@ export class FakeAdvResource {
14
14
  this.__syncitMethod = syncitMethod; // e.g., Syncit.fxDocs or Syncit.fxSheets
15
15
  }
16
16
 
17
- _call(method, params, options, subProp = null) {
17
+ _call(method, params, options, subProp = null, syncMethodOverride = null) {
18
18
  const pack = {
19
19
  prop: this.__serviceName,
20
20
  subProp,
@@ -22,7 +22,8 @@ export class FakeAdvResource {
22
22
  params,
23
23
  options
24
24
  };
25
- const result = this.__syncitMethod(pack);
25
+ const syncMethod = syncMethodOverride || this.__syncitMethod;
26
+ const result = syncMethod(pack);
26
27
  if (!result || !result.response) {
27
28
  // Simulate an error response if the worker didn't provide one.
28
29
  return {
@@ -0,0 +1,47 @@
1
+ import { newFakeGasenum } from "@mcpher/fake-gasenum";
2
+
3
+ export const Button = newFakeGasenum([
4
+ "CANCEL",
5
+ "CLOSE",
6
+ "NO",
7
+ "OK",
8
+ "YES"
9
+ ])
10
+
11
+ export const ButtonSet = newFakeGasenum([
12
+ "OK",
13
+ "OK_CANCEL",
14
+ "YES_NO",
15
+ "YES_NO_CANCEL"
16
+ ])
17
+
18
+ export const ColorType = newFakeGasenum([
19
+ "RGB",
20
+ "THEME",
21
+ "UNSUPPORTED"
22
+ ])
23
+
24
+ export const Month = newFakeGasenum([
25
+ "JANUARY",
26
+ "FEBRUARY",
27
+ "MARCH",
28
+ "APRIL",
29
+ "MAY",
30
+ "JUNE",
31
+ "JULY",
32
+ "AUGUST",
33
+ "SEPTEMBER",
34
+ "OCTOBER",
35
+ "NOVEMBER",
36
+ "DECEMBER"
37
+ ])
38
+
39
+ export const Weekday = newFakeGasenum([
40
+ "SUNDAY",
41
+ "MONDAY",
42
+ "TUESDAY",
43
+ "WEDNESDAY",
44
+ "THURSDAY",
45
+ "FRIDAY",
46
+ "SATURDAY"
47
+ ])
@@ -142,7 +142,9 @@ export const PageType = newFakeGasenum([
142
142
  "UNSUPPORTED",
143
143
  "SLIDE",
144
144
  "LAYOUT",
145
- "MASTER"
145
+ "MASTER",
146
+ "NOTES_MASTER",
147
+ "NOTES_PAGE"
146
148
  ])
147
149
  export const ParagraphAlignment = newFakeGasenum([
148
150
  "UNSUPPORTED",
@@ -60,7 +60,7 @@ export class ServerWorkerContext {
60
60
  mainScriptPath: globalThis.__gasFakesMainScriptPath || this._mainScriptPath,
61
61
  controlBuf: this._controlBuf,
62
62
  dataBuf: this._dataBuf,
63
- env: process.env // Pass current environment
63
+ env: { ...process.env, GAS_FAKES_WORKER: 'true' } // Pass current environment and mark as worker
64
64
  },
65
65
  stdout: true,
66
66
  stderr: true
@@ -5,5 +5,10 @@
5
5
 
6
6
  import { lazyLoaderApp } from '../common/lazyloader.js'
7
7
  import { newFakeSlidesApp as maker} from './fakeslidesapp.js';
8
+ import './pageelementfactory.js';
9
+ export { newFakePageElementRange } from './fakepageelementrange.js';
10
+ export { newFakePageRange } from './fakepagerange.js';
11
+ export { newFakeTableCellRange } from './faketablecellrange.js';
12
+ export { newFakeTableColumn } from './faketablecolumn.js';
8
13
  let _app = null;
9
14
  _app = lazyLoaderApp(_app, 'SlidesApp', maker)
@@ -39,7 +39,7 @@ export class FakeAutofit {
39
39
  }
40
40
  }];
41
41
 
42
- Slides.Presentations.batchUpdate(requests, presentationId);
42
+ Slides.Presentations.batchUpdate({ requests }, presentationId);
43
43
  return this;
44
44
  }
45
45
 
@@ -0,0 +1,106 @@
1
+ import { Proxies } from '../../support/proxies.js';
2
+ import { newFakeLineFill } from './fakelinefill.js';
3
+
4
+ /**
5
+ * Represents the Border properties of a Slides element.
6
+ * Emulates the Slides API resource structure.
7
+ */
8
+ export const newFakeBorder = (...args) => {
9
+ return Proxies.guard(new FakeBorder(...args));
10
+ };
11
+
12
+ export class FakeBorder {
13
+ /**
14
+ * @param {FakePageElement} parent - The element this border belongs to.
15
+ */
16
+ constructor(parent) {
17
+ this._parent = parent;
18
+ }
19
+
20
+ get __resource() {
21
+ // Border properties are typically in imageProperties.outline or shapeProperties.outline
22
+ const res = this._parent.__resource;
23
+ return res.image?.imageProperties?.outline || res.shape?.shapeProperties?.outline;
24
+ }
25
+
26
+ getLineFill() {
27
+ return newFakeLineFill(this._parent);
28
+ }
29
+
30
+ isVisible() {
31
+ const weight = this.getWeight();
32
+ return weight !== null && weight > 0;
33
+ }
34
+
35
+ setDashStyle(style) {
36
+ this.__update({ dashStyle: style.toString() }, 'dashStyle');
37
+ return this;
38
+ }
39
+
40
+ setTransparent() {
41
+ this.__update({ propertyState: 'NOT_RENDERED' }, 'propertyState');
42
+ return this;
43
+ }
44
+
45
+ setWeight(weight) {
46
+ this.__update({ weight: { magnitude: weight, unit: 'PT' }, propertyState: 'RENDERED' }, 'weight,propertyState');
47
+ return this;
48
+ }
49
+
50
+ __update(props, fields) {
51
+ const presentationId = this._parent.__presentation?.getId() || this._parent.__page?.__presentation?.getId();
52
+ const type = this._parent.getPageElementType().toString();
53
+ const objectId = this._parent.getObjectId();
54
+
55
+ let request = null;
56
+ if (type === 'SHAPE') {
57
+ request = {
58
+ updateShapeProperties: {
59
+ objectId,
60
+ shapeProperties: { outline: props },
61
+ fields: fields ? fields.split(',').map(f => `outline.${f}`).join(',') : 'outline'
62
+ }
63
+ };
64
+ } else if (type === 'IMAGE') {
65
+ request = {
66
+ updateImageProperties: {
67
+ objectId,
68
+ imageProperties: { outline: props },
69
+ fields: fields ? fields.split(',').map(f => `outline.${f}`).join(',') : 'outline'
70
+ }
71
+ };
72
+ }
73
+
74
+ if (request) {
75
+ Slides.Presentations.batchUpdate({ requests: [request] }, presentationId);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Retrieves the weight of the border in Points (PT).
81
+ * @returns {number | null} The border weight in PT, or null if not defined/rendered.
82
+ */
83
+ getWeight() {
84
+ const outline = this.__resource;
85
+ if (!outline || outline.propertyState === 'NOT_RENDERED' || outline.weight === undefined) {
86
+ return null;
87
+ }
88
+ return this._parent.__normalize(outline.weight);
89
+ }
90
+
91
+ /**
92
+ * Gets the DashStyle of the border.
93
+ * @returns {string} The dash style.
94
+ */
95
+ getDashStyle() {
96
+ const outline = this.__resource;
97
+ if (!outline || outline.propertyState === 'NOT_RENDERED' || !outline.dashStyle) {
98
+ return 'SOLID';
99
+ }
100
+ return outline.dashStyle;
101
+ }
102
+
103
+ toString() {
104
+ return 'Border';
105
+ }
106
+ }
@@ -150,7 +150,7 @@ export class FakeColorScheme {
150
150
  }
151
151
  }];
152
152
 
153
- Slides.Presentations.batchUpdate(requests, presentationId);
153
+ Slides.Presentations.batchUpdate({ requests }, presentationId);
154
154
  return this;
155
155
  }
156
156
 
@@ -0,0 +1,216 @@
1
+ import { Proxies } from '../../support/proxies.js';
2
+ import { newFakeSolidFill } from './fakesolidfill.js';
3
+ import { FillType } from '../enums/slidesenums.js';
4
+
5
+ export const newFakeFill = (...args) => {
6
+ return Proxies.guard(new FakeFill(...args));
7
+ };
8
+
9
+ export class FakeFill {
10
+ constructor(element) {
11
+ this.__element = element;
12
+ }
13
+
14
+ get __fill() {
15
+ if (this.__element.__resource.shape) {
16
+ return this.__element.__resource.shape.shapeProperties?.shapeBackgroundFill || {};
17
+ }
18
+ if (this.__element.__resource.tableCellProperties) {
19
+ return this.__element.__resource.tableCellProperties.tableCellBackgroundFill || {};
20
+ }
21
+ return this.__element.__resource.pageProperties?.pageBackgroundFill || {};
22
+ }
23
+
24
+ getType() {
25
+ const fill = this.__fill;
26
+ if (fill.solidFill && fill.propertyState !== 'NOT_RENDERED') {
27
+ return FillType.SOLID;
28
+ }
29
+ return FillType.NONE;
30
+ }
31
+
32
+ isVisible() {
33
+ return this.getType().toString() !== 'NONE';
34
+ }
35
+
36
+ getSolidFill() {
37
+ if (this.getType().toString() !== 'SOLID') {
38
+ return null;
39
+ }
40
+ return newFakeSolidFill(this);
41
+ }
42
+
43
+ setSolidFill(...args) {
44
+ let rgbColor = null;
45
+ let themeColor = null;
46
+ let alpha = 1.0;
47
+
48
+ if (args.length >= 3) {
49
+ const r = args[0] / 255;
50
+ const g = args[1] / 255;
51
+ const b = args[2] / 255;
52
+ rgbColor = { red: r, green: g, blue: b };
53
+ if (args.length === 4) {
54
+ alpha = args[3];
55
+ }
56
+ } else if (args.length > 0) {
57
+ const colorArg = args[0];
58
+ if (args.length === 2) {
59
+ alpha = args[1];
60
+ }
61
+
62
+ if (typeof colorArg === 'string') {
63
+ if (colorArg.startsWith('#') || colorArg.length === 6 || colorArg.length === 3) {
64
+ const hex = colorArg.startsWith('#') ? colorArg : '#' + colorArg;
65
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
66
+ if (result) {
67
+ rgbColor = {
68
+ red: parseInt(result[1], 16) / 255.0,
69
+ green: parseInt(result[2], 16) / 255.0,
70
+ blue: parseInt(result[3], 16) / 255.0
71
+ };
72
+ }
73
+ } else {
74
+ themeColor = colorArg.toString();
75
+ }
76
+ } else if (colorArg && typeof colorArg.getColorType === 'function') {
77
+ if (colorArg.getColorType().toString() === 'RGB') {
78
+ const rgb = colorArg.asRgbColor();
79
+ rgbColor = {
80
+ red: rgb.getRed() / 255,
81
+ green: rgb.getGreen() / 255,
82
+ blue: rgb.getBlue() / 255
83
+ };
84
+ } else if (colorArg.getColorType().toString() === 'THEME') {
85
+ themeColor = colorArg.asThemeColor().getThemeColorType().toString();
86
+ }
87
+ } else if (colorArg && colorArg.toString) {
88
+ themeColor = colorArg.toString();
89
+ }
90
+ }
91
+
92
+ const solidFill = {
93
+ color: {}
94
+ };
95
+ if (rgbColor) {
96
+ solidFill.color.rgbColor = rgbColor;
97
+ } else if (themeColor) {
98
+ solidFill.color.themeColor = themeColor;
99
+ }
100
+ if (alpha !== 1.0) {
101
+ solidFill.alpha = alpha;
102
+ }
103
+
104
+ const presentationId = this.__element.__presentation?.getId() || this.__element.__page?.__presentation?.getId() || this.__element.__table?.__presentation?.getId();
105
+ if (this.__element.__resource.shape) {
106
+ Slides.Presentations.batchUpdate({ requests: [{
107
+ updateShapeProperties: {
108
+ objectId: this.__element.getObjectId(),
109
+ shapeProperties: {
110
+ shapeBackgroundFill: {
111
+ propertyState: 'RENDERED',
112
+ solidFill: solidFill
113
+ }
114
+ },
115
+ fields: 'shapeBackgroundFill'
116
+ }
117
+ }] }, presentationId);
118
+
119
+ } else if (this.__element.__resource.tableCellProperties) {
120
+ Slides.Presentations.batchUpdate({ requests: [{
121
+ updateTableCellProperties: {
122
+ objectId: this.__element.getObjectId(),
123
+ tableCellProperties: {
124
+ tableCellBackgroundFill: {
125
+ propertyState: 'RENDERED',
126
+ solidFill: solidFill
127
+ }
128
+ },
129
+ fields: 'tableCellBackgroundFill',
130
+ tableRange: {
131
+ location: {
132
+ rowIndex: this.__element.getRowIndex(),
133
+ columnIndex: this.__element.getColumnIndex()
134
+ },
135
+ rowSpan: 1,
136
+ columnSpan: 1
137
+ }
138
+ }
139
+ }] }, presentationId);
140
+
141
+ } else {
142
+ Slides.Presentations.batchUpdate({ requests: [{
143
+ updatePageProperties: {
144
+ objectId: this.__element.getObjectId(),
145
+ pageProperties: {
146
+ pageBackgroundFill: {
147
+ propertyState: 'RENDERED',
148
+ solidFill: solidFill
149
+ }
150
+ },
151
+ fields: 'pageBackgroundFill'
152
+ }
153
+ }] }, presentationId);
154
+
155
+ }
156
+
157
+ return this;
158
+ }
159
+
160
+ setTransparent() {
161
+ const presentationId = this.__element.__presentation?.getId() || this.__element.__page?.__presentation?.getId() || this.__element.__table?.__presentation?.getId();
162
+ if (this.__element.__resource.shape) {
163
+ Slides.Presentations.batchUpdate({ requests: [{
164
+ updateShapeProperties: {
165
+ objectId: this.__element.getObjectId(),
166
+ shapeProperties: {
167
+ shapeBackgroundFill: {
168
+ propertyState: 'NOT_RENDERED'
169
+ }
170
+ },
171
+ fields: 'shapeBackgroundFill'
172
+ }
173
+ }] }, presentationId);
174
+
175
+ } else if (this.__element.__resource.tableCellProperties) {
176
+ Slides.Presentations.batchUpdate({ requests: [{
177
+ updateTableCellProperties: {
178
+ objectId: this.__element.getObjectId(),
179
+ tableCellProperties: {
180
+ tableCellBackgroundFill: {
181
+ propertyState: 'NOT_RENDERED'
182
+ }
183
+ },
184
+ fields: 'tableCellBackgroundFill',
185
+ tableRange: {
186
+ location: {
187
+ rowIndex: this.__element.getRowIndex(),
188
+ columnIndex: this.__element.getColumnIndex()
189
+ },
190
+ rowSpan: 1,
191
+ columnSpan: 1
192
+ }
193
+ }
194
+ }] }, presentationId);
195
+
196
+ } else {
197
+ Slides.Presentations.batchUpdate({ requests: [{
198
+ updatePageProperties: {
199
+ objectId: this.__element.getObjectId(),
200
+ pageProperties: {
201
+ pageBackgroundFill: {
202
+ propertyState: 'NOT_RENDERED'
203
+ }
204
+ },
205
+ fields: 'pageBackgroundFill'
206
+ }
207
+ }] }, presentationId);
208
+
209
+ }
210
+ return this;
211
+ }
212
+
213
+ toString() {
214
+ return 'Fill';
215
+ }
216
+ }
@@ -0,0 +1,35 @@
1
+ import { Proxies } from '../../support/proxies.js';
2
+ import { FakePageElement, PageElementRegistry } from './fakepageelement.js';
3
+
4
+ export const newFakeGroup = (...args) => {
5
+ return Proxies.guard(new FakeGroup(...args));
6
+ };
7
+
8
+ PageElementRegistry.newFakeGroup = newFakeGroup;
9
+
10
+ export class FakeGroup extends FakePageElement {
11
+ constructor(resource, page) {
12
+ super(resource, page);
13
+ }
14
+
15
+ getChildren() {
16
+ const children = this.__resource.elementGroup?.children || this.__resource.group?.children || [];
17
+ const { newFakePageElement } = PageElementRegistry;
18
+ return children.map(childResource => {
19
+ return newFakePageElement(childResource, this.__page);
20
+ });
21
+ }
22
+
23
+ ungroup() {
24
+ const presentationId = this.__page.__presentation?.getId() || this.__page.__slide?.__presentation.getId();
25
+ Slides.Presentations.batchUpdate({ requests: [{
26
+ ungroupObjects: {
27
+ objectIds: [this.getObjectId()]
28
+ }
29
+ }] }, presentationId);
30
+ }
31
+
32
+ toString() {
33
+ return 'Group';
34
+ }
35
+ }