runline 0.11.2 → 0.11.4
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.
- package/dist/plugin/loader.js +5 -0
- package/dist/plugins/googleDocs/src/documents.js +91 -0
- package/dist/plugins/googleDocs/src/formatting.js +126 -0
- package/dist/plugins/googleDocs/src/images.js +66 -0
- package/dist/plugins/googleDocs/src/index.js +45 -1008
- package/dist/plugins/googleDocs/src/shared.js +110 -0
- package/dist/plugins/googleDocs/src/structure.js +392 -0
- package/dist/plugins/googleDocs/src/tables.js +390 -0
- package/dist/plugins/googleDocs/src/tabs.js +77 -0
- package/dist/plugins/googleDocs/src/text.js +313 -0
- package/dist/plugins/linear/src/attachments.js +36 -12
- package/dist/plugins/linear/src/comments.js +20 -8
- package/dist/plugins/linear/src/cycles.js +22 -8
- package/dist/plugins/linear/src/initiatives.js +59 -19
- package/dist/plugins/linear/src/issues.js +123 -41
- package/dist/plugins/linear/src/labels.js +31 -11
- package/dist/plugins/linear/src/organization.js +1 -1
- package/dist/plugins/linear/src/projects.js +171 -57
- package/dist/plugins/linear/src/shared.js +16 -5
- package/dist/plugins/linear/src/states.js +14 -6
- package/dist/plugins/linear/src/teams.js +35 -12
- package/dist/plugins/linear/src/users.js +7 -3
- package/dist/plugins/linear/src/views.js +29 -10
- package/dist/plugins/linear/src/webhooks.js +39 -13
- package/dist/plugins/salesforce/src/index.js +33 -219
- package/dist/plugins/salesforce/src/metadata.js +50 -0
- package/dist/plugins/salesforce/src/query.js +52 -0
- package/dist/plugins/salesforce/src/queryResult.js +4 -0
- package/dist/plugins/salesforce/src/shared.js +122 -0
- package/dist/plugins/salesforce/src/sobjects.js +152 -0
- package/package.json +1 -1
|
@@ -6,145 +6,15 @@
|
|
|
6
6
|
* because `document.create` goes through Drive's files endpoint
|
|
7
7
|
* — the Docs API itself only creates blank documents without a
|
|
8
8
|
* target folder.
|
|
9
|
-
*
|
|
10
|
-
* Surface area:
|
|
11
|
-
*
|
|
12
|
-
* document.create
|
|
13
|
-
* document.get (optional `simple=true` returns flat text)
|
|
14
|
-
* document.batchUpdate (raw request list)
|
|
15
|
-
*
|
|
16
|
-
* Plus convenience helpers that wrap the most common batchUpdate
|
|
17
|
-
* shapes as first-class actions, addressable without constructing
|
|
18
|
-
* the nested request objects by hand:
|
|
19
|
-
*
|
|
20
|
-
* document.insertText
|
|
21
|
-
* document.replaceAllText
|
|
22
|
-
* document.deleteContentRange
|
|
23
|
-
* document.insertTable
|
|
24
|
-
* document.insertPageBreak
|
|
25
|
-
* document.createParagraphBullets
|
|
26
|
-
* document.deleteParagraphBullets
|
|
27
|
-
* document.createNamedRange
|
|
28
|
-
* document.deleteNamedRange
|
|
29
|
-
* document.createHeader / document.deleteHeader
|
|
30
|
-
* document.createFooter / document.deleteFooter
|
|
31
|
-
* document.deletePositionedObject
|
|
32
|
-
* document.insertTableRow / document.deleteTableRow
|
|
33
|
-
* document.insertTableColumn / document.deleteTableColumn
|
|
34
|
-
*
|
|
35
|
-
* Every helper ultimately hits `POST /v1/documents/{id}:batchUpdate`
|
|
36
|
-
* with a single request; callers who need to chain multiple edits
|
|
37
|
-
* atomically can compose them via `document.batchUpdate`.
|
|
38
|
-
*/
|
|
39
|
-
import { googleAccessToken } from "../../_shared/googleAuth.js";
|
|
40
|
-
// ─── Auth ────────────────────────────────────────────────────────
|
|
41
|
-
async function accessToken(ctx) {
|
|
42
|
-
return googleAccessToken(ctx, "googleDocs", SCOPES);
|
|
43
|
-
}
|
|
44
|
-
// ─── Request ─────────────────────────────────────────────────────
|
|
45
|
-
const DOCS_BASE = "https://docs.googleapis.com/v1";
|
|
46
|
-
const DRIVE_BASE = "https://www.googleapis.com/drive/v3";
|
|
47
|
-
async function docsRequest(ctx, method, path, body, qs, baseOverride) {
|
|
48
|
-
const token = await accessToken(ctx);
|
|
49
|
-
const url = new URL(`${baseOverride ?? DOCS_BASE}${path}`);
|
|
50
|
-
if (qs) {
|
|
51
|
-
for (const [k, v] of Object.entries(qs)) {
|
|
52
|
-
if (v === undefined || v === null)
|
|
53
|
-
continue;
|
|
54
|
-
url.searchParams.set(k, String(v));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
const init = {
|
|
58
|
-
method,
|
|
59
|
-
headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
|
|
60
|
-
};
|
|
61
|
-
if (body && Object.keys(body).length > 0) {
|
|
62
|
-
init.headers["Content-Type"] = "application/json";
|
|
63
|
-
init.body = JSON.stringify(body);
|
|
64
|
-
}
|
|
65
|
-
const res = await fetch(url.toString(), init);
|
|
66
|
-
if (res.status === 204)
|
|
67
|
-
return { success: true };
|
|
68
|
-
const text = await res.text();
|
|
69
|
-
if (!res.ok) {
|
|
70
|
-
throw new Error(`googleDocs: ${method} ${path} → ${res.status} ${text}`);
|
|
71
|
-
}
|
|
72
|
-
return text ? JSON.parse(text) : { success: true };
|
|
73
|
-
}
|
|
74
|
-
// ─── Helpers ────────────────────────────────────────────────────
|
|
75
|
-
const DOC_URL_REGEX = /https:\/\/docs\.google\.com\/document\/d\/([a-zA-Z0-9-_]+)/;
|
|
76
|
-
/**
|
|
77
|
-
* Accept a bare document ID or a full docs.google.com URL and return
|
|
78
|
-
* the ID. Falls through to the input unchanged if no URL is detected.
|
|
79
|
-
*/
|
|
80
|
-
function extractDocumentId(input) {
|
|
81
|
-
if (!input)
|
|
82
|
-
throw new Error("googleDocs: documentId or URL is required");
|
|
83
|
-
const m = input.match(DOC_URL_REGEX);
|
|
84
|
-
return m ? m[1] : input;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Build a `Location` object for Docs insert requests. When
|
|
88
|
-
* `segmentId` is "body" or missing, send an empty segmentId — Docs
|
|
89
|
-
* treats that as "the document body". `index` is required for
|
|
90
|
-
* `location`; `endOfSegmentLocation` doesn't take one.
|
|
91
|
-
*/
|
|
92
|
-
function buildLocation(kind, segmentId, index) {
|
|
93
|
-
const seg = segmentId && segmentId !== "body" ? segmentId : "";
|
|
94
|
-
if (kind === "endOfSegmentLocation") {
|
|
95
|
-
return { endOfSegmentLocation: { segmentId: seg } };
|
|
96
|
-
}
|
|
97
|
-
if (index === undefined || index === null) {
|
|
98
|
-
throw new Error("googleDocs: `index` is required when location kind is 'location'");
|
|
99
|
-
}
|
|
100
|
-
return { location: { segmentId: seg, index } };
|
|
101
|
-
}
|
|
102
|
-
async function runBatchUpdate(ctx, documentId, request, writeControl) {
|
|
103
|
-
const body = { requests: [request] };
|
|
104
|
-
if (writeControl)
|
|
105
|
-
body.writeControl = writeControl;
|
|
106
|
-
const res = (await docsRequest(ctx, "POST", `/documents/${documentId}:batchUpdate`, body));
|
|
107
|
-
// Flatten single-request replies so callers don't have to drill in.
|
|
108
|
-
const reply = res.replies?.[0] ?? {};
|
|
109
|
-
const key = Object.keys(reply)[0];
|
|
110
|
-
return { documentId, ...(key ? { [key]: reply[key] } : {}) };
|
|
111
|
-
}
|
|
112
|
-
/**
|
|
113
|
-
* Walk a `document.body.content` tree and concatenate every
|
|
114
|
-
* `textRun.content` we find — the `simple=true` output on
|
|
115
|
-
* `document.get`. Intentionally ignores tables, headers, footers,
|
|
116
|
-
* and inline objects.
|
|
117
9
|
*/
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
const tr = el.textRun;
|
|
127
|
-
if (tr?.content)
|
|
128
|
-
parts.push(tr.content);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return parts.join("");
|
|
132
|
-
}
|
|
133
|
-
// ─── Plugin ──────────────────────────────────────────────────────
|
|
134
|
-
const SCOPES = [
|
|
135
|
-
"https://www.googleapis.com/auth/documents",
|
|
136
|
-
"https://www.googleapis.com/auth/drive.file",
|
|
137
|
-
];
|
|
138
|
-
function hexToRgbF(hex) {
|
|
139
|
-
const h = hex.replace(/^#/, "");
|
|
140
|
-
const full = h.length === 3 ? h.split("").map((c) => c + c).join("") : h;
|
|
141
|
-
const n = parseInt(full, 16);
|
|
142
|
-
return {
|
|
143
|
-
red: ((n >> 16) & 0xff) / 255,
|
|
144
|
-
green: ((n >> 8) & 0xff) / 255,
|
|
145
|
-
blue: (n & 0xff) / 255,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
10
|
+
import { registerDocumentsActions } from "./documents.js";
|
|
11
|
+
import { registerFormattingActions } from "./formatting.js";
|
|
12
|
+
import { registerImagesActions } from "./images.js";
|
|
13
|
+
import { SCOPES } from "./shared.js";
|
|
14
|
+
import { registerStructureActions } from "./structure.js";
|
|
15
|
+
import { registerTablesActions } from "./tables.js";
|
|
16
|
+
import { registerTabActions } from "./tabs.js";
|
|
17
|
+
import { registerTextActions } from "./text.js";
|
|
148
18
|
export default function googleDocs(rl) {
|
|
149
19
|
rl.setName("googleDocs");
|
|
150
20
|
rl.setVersion("0.1.0");
|
|
@@ -182,877 +52,44 @@ export default function googleDocs(rl) {
|
|
|
182
52
|
});
|
|
183
53
|
rl.setConnectionSchema({
|
|
184
54
|
clientId: { type: "string", required: false, env: "GOOGLE_DOCS_CLIENT_ID" },
|
|
185
|
-
clientSecret: {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
55
|
+
clientSecret: {
|
|
56
|
+
type: "string",
|
|
57
|
+
required: false,
|
|
58
|
+
env: "GOOGLE_DOCS_CLIENT_SECRET",
|
|
59
|
+
},
|
|
60
|
+
refreshToken: {
|
|
61
|
+
type: "string",
|
|
62
|
+
required: false,
|
|
63
|
+
env: "GOOGLE_DOCS_REFRESH_TOKEN",
|
|
64
|
+
},
|
|
65
|
+
serviceAccountJson: {
|
|
66
|
+
type: "string",
|
|
67
|
+
required: false,
|
|
68
|
+
env: "GOOGLE_DOCS_SERVICE_ACCOUNT_JSON",
|
|
69
|
+
},
|
|
70
|
+
serviceAccountEmail: {
|
|
71
|
+
type: "string",
|
|
72
|
+
required: false,
|
|
73
|
+
env: "GOOGLE_DOCS_SERVICE_ACCOUNT_EMAIL",
|
|
74
|
+
},
|
|
75
|
+
serviceAccountPrivateKey: {
|
|
76
|
+
type: "string",
|
|
77
|
+
required: false,
|
|
78
|
+
env: "GOOGLE_DOCS_SERVICE_ACCOUNT_PRIVATE_KEY",
|
|
79
|
+
},
|
|
80
|
+
serviceAccountSubject: {
|
|
81
|
+
type: "string",
|
|
82
|
+
required: false,
|
|
83
|
+
env: "GOOGLE_DOCS_SERVICE_ACCOUNT_SUBJECT",
|
|
84
|
+
},
|
|
191
85
|
accessToken: { type: "string", required: false },
|
|
192
86
|
accessTokenExpiresAt: { type: "number", required: false },
|
|
193
87
|
});
|
|
194
|
-
|
|
195
|
-
rl
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
required: false,
|
|
202
|
-
description: "Parent folder in Drive. Omit to place in My Drive root.",
|
|
203
|
-
},
|
|
204
|
-
},
|
|
205
|
-
async execute(input, ctx) {
|
|
206
|
-
const p = (input ?? {});
|
|
207
|
-
const body = {
|
|
208
|
-
name: p.title,
|
|
209
|
-
mimeType: "application/vnd.google-apps.document",
|
|
210
|
-
};
|
|
211
|
-
if (p.folderId) {
|
|
212
|
-
body.parents = [p.folderId];
|
|
213
|
-
}
|
|
214
|
-
return docsRequest(ctx, "POST", "/files", body, undefined, DRIVE_BASE);
|
|
215
|
-
},
|
|
216
|
-
});
|
|
217
|
-
rl.registerAction("document.get", {
|
|
218
|
-
description: "Get a document. Accepts a bare ID or a docs.google.com URL. `simple=true` collapses the body to plain text.",
|
|
219
|
-
inputSchema: {
|
|
220
|
-
document: { type: "string", required: true, description: "Document ID or URL" },
|
|
221
|
-
simple: { type: "boolean", required: false },
|
|
222
|
-
suggestionsViewMode: {
|
|
223
|
-
type: "string",
|
|
224
|
-
required: false,
|
|
225
|
-
description: "DEFAULT_FOR_CURRENT_ACCESS | SUGGESTIONS_INLINE | PREVIEW_SUGGESTIONS_ACCEPTED | PREVIEW_WITHOUT_SUGGESTIONS",
|
|
226
|
-
},
|
|
227
|
-
},
|
|
228
|
-
async execute(input, ctx) {
|
|
229
|
-
const p = (input ?? {});
|
|
230
|
-
const documentId = extractDocumentId(p.document);
|
|
231
|
-
const qs = {};
|
|
232
|
-
if (p.suggestionsViewMode)
|
|
233
|
-
qs.suggestionsViewMode = p.suggestionsViewMode;
|
|
234
|
-
const res = (await docsRequest(ctx, "GET", `/documents/${documentId}`, undefined, qs));
|
|
235
|
-
if (!p.simple)
|
|
236
|
-
return res;
|
|
237
|
-
return { documentId, content: flattenBodyText(res.body) };
|
|
238
|
-
},
|
|
239
|
-
});
|
|
240
|
-
rl.registerAction("document.batchUpdate", {
|
|
241
|
-
description: "Raw passthrough to documents.batchUpdate — pass a full `requests` array for atomic multi-edit operations.",
|
|
242
|
-
inputSchema: {
|
|
243
|
-
document: { type: "string", required: true },
|
|
244
|
-
requests: { type: "array", required: true },
|
|
245
|
-
writeControl: {
|
|
246
|
-
type: "object",
|
|
247
|
-
required: false,
|
|
248
|
-
description: "{requiredRevisionId} | {targetRevisionId}",
|
|
249
|
-
},
|
|
250
|
-
},
|
|
251
|
-
async execute(input, ctx) {
|
|
252
|
-
const p = (input ?? {});
|
|
253
|
-
const documentId = extractDocumentId(p.document);
|
|
254
|
-
const body = {
|
|
255
|
-
requests: p.requests,
|
|
256
|
-
};
|
|
257
|
-
if (p.writeControl)
|
|
258
|
-
body.writeControl = p.writeControl;
|
|
259
|
-
return docsRequest(ctx, "POST", `/documents/${documentId}:batchUpdate`, body);
|
|
260
|
-
},
|
|
261
|
-
});
|
|
262
|
-
// ── Text edits ────────────────────────────────────────
|
|
263
|
-
rl.registerAction("document.insertText", {
|
|
264
|
-
description: "Insert text at a specific index, or at the end of a segment (body/header/footer/footnote).",
|
|
265
|
-
inputSchema: {
|
|
266
|
-
document: { type: "string", required: true },
|
|
267
|
-
text: { type: "string", required: true },
|
|
268
|
-
locationKind: {
|
|
269
|
-
type: "string",
|
|
270
|
-
required: false,
|
|
271
|
-
description: "location (default; requires index) | endOfSegmentLocation",
|
|
272
|
-
},
|
|
273
|
-
index: { type: "number", required: false, description: "Required for locationKind=location" },
|
|
274
|
-
segmentId: {
|
|
275
|
-
type: "string",
|
|
276
|
-
required: false,
|
|
277
|
-
description: 'Segment ID, or "body" / empty for the main body',
|
|
278
|
-
},
|
|
279
|
-
},
|
|
280
|
-
async execute(input, ctx) {
|
|
281
|
-
const p = (input ?? {});
|
|
282
|
-
const documentId = extractDocumentId(p.document);
|
|
283
|
-
const kind = p.locationKind ?? "location";
|
|
284
|
-
const locObj = buildLocation(kind, p.segmentId, p.index);
|
|
285
|
-
return runBatchUpdate(ctx, documentId, {
|
|
286
|
-
insertText: { text: p.text, ...locObj },
|
|
287
|
-
});
|
|
288
|
-
},
|
|
289
|
-
});
|
|
290
|
-
rl.registerAction("document.replaceAllText", {
|
|
291
|
-
description: "Replace every occurrence of a text string throughout the document.",
|
|
292
|
-
inputSchema: {
|
|
293
|
-
document: { type: "string", required: true },
|
|
294
|
-
findText: { type: "string", required: true },
|
|
295
|
-
replaceText: { type: "string", required: true },
|
|
296
|
-
matchCase: { type: "boolean", required: false },
|
|
297
|
-
},
|
|
298
|
-
async execute(input, ctx) {
|
|
299
|
-
const p = (input ?? {});
|
|
300
|
-
const documentId = extractDocumentId(p.document);
|
|
301
|
-
return runBatchUpdate(ctx, documentId, {
|
|
302
|
-
replaceAllText: {
|
|
303
|
-
replaceText: p.replaceText,
|
|
304
|
-
containsText: { text: p.findText, matchCase: p.matchCase === true },
|
|
305
|
-
},
|
|
306
|
-
});
|
|
307
|
-
},
|
|
308
|
-
});
|
|
309
|
-
rl.registerAction("document.deleteContentRange", {
|
|
310
|
-
description: "Delete text between two indices in a segment.",
|
|
311
|
-
inputSchema: {
|
|
312
|
-
document: { type: "string", required: true },
|
|
313
|
-
startIndex: { type: "number", required: true },
|
|
314
|
-
endIndex: { type: "number", required: true },
|
|
315
|
-
segmentId: { type: "string", required: false },
|
|
316
|
-
},
|
|
317
|
-
async execute(input, ctx) {
|
|
318
|
-
const p = (input ?? {});
|
|
319
|
-
const documentId = extractDocumentId(p.document);
|
|
320
|
-
const seg = p.segmentId && p.segmentId !== "body" ? p.segmentId : "";
|
|
321
|
-
return runBatchUpdate(ctx, documentId, {
|
|
322
|
-
deleteContentRange: {
|
|
323
|
-
range: {
|
|
324
|
-
segmentId: seg,
|
|
325
|
-
startIndex: p.startIndex,
|
|
326
|
-
endIndex: p.endIndex,
|
|
327
|
-
},
|
|
328
|
-
},
|
|
329
|
-
});
|
|
330
|
-
},
|
|
331
|
-
});
|
|
332
|
-
// ── Structural inserts ────────────────────────────────
|
|
333
|
-
rl.registerAction("document.insertPageBreak", {
|
|
334
|
-
description: "Insert a page break at an index or at the end of a segment.",
|
|
335
|
-
inputSchema: {
|
|
336
|
-
document: { type: "string", required: true },
|
|
337
|
-
locationKind: { type: "string", required: false },
|
|
338
|
-
index: { type: "number", required: false },
|
|
339
|
-
segmentId: { type: "string", required: false },
|
|
340
|
-
},
|
|
341
|
-
async execute(input, ctx) {
|
|
342
|
-
const p = (input ?? {});
|
|
343
|
-
const documentId = extractDocumentId(p.document);
|
|
344
|
-
const kind = p.locationKind ?? "location";
|
|
345
|
-
return runBatchUpdate(ctx, documentId, {
|
|
346
|
-
insertPageBreak: buildLocation(kind, p.segmentId, p.index),
|
|
347
|
-
});
|
|
348
|
-
},
|
|
349
|
-
});
|
|
350
|
-
rl.registerAction("document.insertTable", {
|
|
351
|
-
description: "Insert an empty table with the given dimensions.",
|
|
352
|
-
inputSchema: {
|
|
353
|
-
document: { type: "string", required: true },
|
|
354
|
-
rows: { type: "number", required: true },
|
|
355
|
-
columns: { type: "number", required: true },
|
|
356
|
-
locationKind: { type: "string", required: false },
|
|
357
|
-
index: { type: "number", required: false },
|
|
358
|
-
segmentId: { type: "string", required: false },
|
|
359
|
-
},
|
|
360
|
-
async execute(input, ctx) {
|
|
361
|
-
const p = (input ?? {});
|
|
362
|
-
const documentId = extractDocumentId(p.document);
|
|
363
|
-
const kind = p.locationKind ?? "location";
|
|
364
|
-
return runBatchUpdate(ctx, documentId, {
|
|
365
|
-
insertTable: {
|
|
366
|
-
rows: p.rows,
|
|
367
|
-
columns: p.columns,
|
|
368
|
-
...buildLocation(kind, p.segmentId, p.index),
|
|
369
|
-
},
|
|
370
|
-
});
|
|
371
|
-
},
|
|
372
|
-
});
|
|
373
|
-
rl.registerAction("document.insertTableRow", {
|
|
374
|
-
description: "Insert a table row above or below a cell in an existing table.",
|
|
375
|
-
inputSchema: {
|
|
376
|
-
document: { type: "string", required: true },
|
|
377
|
-
tableStartIndex: {
|
|
378
|
-
type: "number",
|
|
379
|
-
required: true,
|
|
380
|
-
description: "Document index where the table begins",
|
|
381
|
-
},
|
|
382
|
-
rowIndex: { type: "number", required: true },
|
|
383
|
-
columnIndex: { type: "number", required: true },
|
|
384
|
-
insertBelow: { type: "boolean", required: false, description: "default: false (insert above)" },
|
|
385
|
-
segmentId: { type: "string", required: false },
|
|
386
|
-
},
|
|
387
|
-
async execute(input, ctx) {
|
|
388
|
-
const p = (input ?? {});
|
|
389
|
-
const documentId = extractDocumentId(p.document);
|
|
390
|
-
const seg = p.segmentId && p.segmentId !== "body" ? p.segmentId : "";
|
|
391
|
-
return runBatchUpdate(ctx, documentId, {
|
|
392
|
-
insertTableRow: {
|
|
393
|
-
insertBelow: p.insertBelow === true,
|
|
394
|
-
tableCellLocation: {
|
|
395
|
-
rowIndex: p.rowIndex,
|
|
396
|
-
columnIndex: p.columnIndex,
|
|
397
|
-
tableStartLocation: { segmentId: seg, index: p.tableStartIndex },
|
|
398
|
-
},
|
|
399
|
-
},
|
|
400
|
-
});
|
|
401
|
-
},
|
|
402
|
-
});
|
|
403
|
-
rl.registerAction("document.deleteTableRow", {
|
|
404
|
-
description: "Delete a specific row from a table.",
|
|
405
|
-
inputSchema: {
|
|
406
|
-
document: { type: "string", required: true },
|
|
407
|
-
tableStartIndex: { type: "number", required: true },
|
|
408
|
-
rowIndex: { type: "number", required: true },
|
|
409
|
-
columnIndex: { type: "number", required: true },
|
|
410
|
-
segmentId: { type: "string", required: false },
|
|
411
|
-
},
|
|
412
|
-
async execute(input, ctx) {
|
|
413
|
-
const p = (input ?? {});
|
|
414
|
-
const documentId = extractDocumentId(p.document);
|
|
415
|
-
const seg = p.segmentId && p.segmentId !== "body" ? p.segmentId : "";
|
|
416
|
-
return runBatchUpdate(ctx, documentId, {
|
|
417
|
-
deleteTableRow: {
|
|
418
|
-
tableCellLocation: {
|
|
419
|
-
rowIndex: p.rowIndex,
|
|
420
|
-
columnIndex: p.columnIndex,
|
|
421
|
-
tableStartLocation: { segmentId: seg, index: p.tableStartIndex },
|
|
422
|
-
},
|
|
423
|
-
},
|
|
424
|
-
});
|
|
425
|
-
},
|
|
426
|
-
});
|
|
427
|
-
rl.registerAction("document.insertTableColumn", {
|
|
428
|
-
description: "Insert a column left or right of a cell.",
|
|
429
|
-
inputSchema: {
|
|
430
|
-
document: { type: "string", required: true },
|
|
431
|
-
tableStartIndex: { type: "number", required: true },
|
|
432
|
-
rowIndex: { type: "number", required: true },
|
|
433
|
-
columnIndex: { type: "number", required: true },
|
|
434
|
-
insertRight: { type: "boolean", required: false, description: "default: false (insert left)" },
|
|
435
|
-
segmentId: { type: "string", required: false },
|
|
436
|
-
},
|
|
437
|
-
async execute(input, ctx) {
|
|
438
|
-
const p = (input ?? {});
|
|
439
|
-
const documentId = extractDocumentId(p.document);
|
|
440
|
-
const seg = p.segmentId && p.segmentId !== "body" ? p.segmentId : "";
|
|
441
|
-
return runBatchUpdate(ctx, documentId, {
|
|
442
|
-
insertTableColumn: {
|
|
443
|
-
insertRight: p.insertRight === true,
|
|
444
|
-
tableCellLocation: {
|
|
445
|
-
rowIndex: p.rowIndex,
|
|
446
|
-
columnIndex: p.columnIndex,
|
|
447
|
-
tableStartLocation: { segmentId: seg, index: p.tableStartIndex },
|
|
448
|
-
},
|
|
449
|
-
},
|
|
450
|
-
});
|
|
451
|
-
},
|
|
452
|
-
});
|
|
453
|
-
rl.registerAction("document.deleteTableColumn", {
|
|
454
|
-
description: "Delete a specific column from a table.",
|
|
455
|
-
inputSchema: {
|
|
456
|
-
document: { type: "string", required: true },
|
|
457
|
-
tableStartIndex: { type: "number", required: true },
|
|
458
|
-
rowIndex: { type: "number", required: true },
|
|
459
|
-
columnIndex: { type: "number", required: true },
|
|
460
|
-
segmentId: { type: "string", required: false },
|
|
461
|
-
},
|
|
462
|
-
async execute(input, ctx) {
|
|
463
|
-
const p = (input ?? {});
|
|
464
|
-
const documentId = extractDocumentId(p.document);
|
|
465
|
-
const seg = p.segmentId && p.segmentId !== "body" ? p.segmentId : "";
|
|
466
|
-
return runBatchUpdate(ctx, documentId, {
|
|
467
|
-
deleteTableColumn: {
|
|
468
|
-
tableCellLocation: {
|
|
469
|
-
rowIndex: p.rowIndex,
|
|
470
|
-
columnIndex: p.columnIndex,
|
|
471
|
-
tableStartLocation: { segmentId: seg, index: p.tableStartIndex },
|
|
472
|
-
},
|
|
473
|
-
},
|
|
474
|
-
});
|
|
475
|
-
},
|
|
476
|
-
});
|
|
477
|
-
// ── Bullets ───────────────────────────────────────────
|
|
478
|
-
rl.registerAction("document.createParagraphBullets", {
|
|
479
|
-
description: "Apply a bullet preset to paragraphs spanning a range. Presets: BULLET_DISC_CIRCLE_SQUARE, BULLET_DIAMONDX_ARROW3D_SQUARE, BULLET_CHECKBOX, NUMBERED_DECIMAL_ALPHA_ROMAN, NUMBERED_DECIMAL_NESTED, etc.",
|
|
480
|
-
inputSchema: {
|
|
481
|
-
document: { type: "string", required: true },
|
|
482
|
-
bulletPreset: { type: "string", required: true },
|
|
483
|
-
startIndex: { type: "number", required: true },
|
|
484
|
-
endIndex: { type: "number", required: true },
|
|
485
|
-
segmentId: { type: "string", required: false },
|
|
486
|
-
},
|
|
487
|
-
async execute(input, ctx) {
|
|
488
|
-
const p = (input ?? {});
|
|
489
|
-
const documentId = extractDocumentId(p.document);
|
|
490
|
-
const seg = p.segmentId && p.segmentId !== "body" ? p.segmentId : "";
|
|
491
|
-
return runBatchUpdate(ctx, documentId, {
|
|
492
|
-
createParagraphBullets: {
|
|
493
|
-
bulletPreset: p.bulletPreset,
|
|
494
|
-
range: { segmentId: seg, startIndex: p.startIndex, endIndex: p.endIndex },
|
|
495
|
-
},
|
|
496
|
-
});
|
|
497
|
-
},
|
|
498
|
-
});
|
|
499
|
-
rl.registerAction("document.deleteParagraphBullets", {
|
|
500
|
-
description: "Remove bullets from paragraphs in a range.",
|
|
501
|
-
inputSchema: {
|
|
502
|
-
document: { type: "string", required: true },
|
|
503
|
-
startIndex: { type: "number", required: true },
|
|
504
|
-
endIndex: { type: "number", required: true },
|
|
505
|
-
segmentId: { type: "string", required: false },
|
|
506
|
-
},
|
|
507
|
-
async execute(input, ctx) {
|
|
508
|
-
const p = (input ?? {});
|
|
509
|
-
const documentId = extractDocumentId(p.document);
|
|
510
|
-
const seg = p.segmentId && p.segmentId !== "body" ? p.segmentId : "";
|
|
511
|
-
return runBatchUpdate(ctx, documentId, {
|
|
512
|
-
deleteParagraphBullets: {
|
|
513
|
-
range: { segmentId: seg, startIndex: p.startIndex, endIndex: p.endIndex },
|
|
514
|
-
},
|
|
515
|
-
});
|
|
516
|
-
},
|
|
517
|
-
});
|
|
518
|
-
// ── Named ranges ──────────────────────────────────────
|
|
519
|
-
rl.registerAction("document.createNamedRange", {
|
|
520
|
-
description: "Create a named range over a span of text (useful for later programmatic edits).",
|
|
521
|
-
inputSchema: {
|
|
522
|
-
document: { type: "string", required: true },
|
|
523
|
-
name: { type: "string", required: true },
|
|
524
|
-
startIndex: { type: "number", required: true },
|
|
525
|
-
endIndex: { type: "number", required: true },
|
|
526
|
-
segmentId: { type: "string", required: false },
|
|
527
|
-
},
|
|
528
|
-
async execute(input, ctx) {
|
|
529
|
-
const p = (input ?? {});
|
|
530
|
-
const documentId = extractDocumentId(p.document);
|
|
531
|
-
const seg = p.segmentId && p.segmentId !== "body" ? p.segmentId : "";
|
|
532
|
-
return runBatchUpdate(ctx, documentId, {
|
|
533
|
-
createNamedRange: {
|
|
534
|
-
name: p.name,
|
|
535
|
-
range: { segmentId: seg, startIndex: p.startIndex, endIndex: p.endIndex },
|
|
536
|
-
},
|
|
537
|
-
});
|
|
538
|
-
},
|
|
539
|
-
});
|
|
540
|
-
rl.registerAction("document.deleteNamedRange", {
|
|
541
|
-
description: "Delete named range(s). Pass one of `namedRangeId` or `name`; the latter deletes every range sharing that name.",
|
|
542
|
-
inputSchema: {
|
|
543
|
-
document: { type: "string", required: true },
|
|
544
|
-
namedRangeId: { type: "string", required: false },
|
|
545
|
-
name: { type: "string", required: false },
|
|
546
|
-
},
|
|
547
|
-
async execute(input, ctx) {
|
|
548
|
-
const p = (input ?? {});
|
|
549
|
-
const documentId = extractDocumentId(p.document);
|
|
550
|
-
if (!p.namedRangeId && !p.name) {
|
|
551
|
-
throw new Error("googleDocs: provide namedRangeId or name");
|
|
552
|
-
}
|
|
553
|
-
const req = p.namedRangeId
|
|
554
|
-
? { namedRangeId: p.namedRangeId }
|
|
555
|
-
: { name: p.name };
|
|
556
|
-
return runBatchUpdate(ctx, documentId, { deleteNamedRange: req });
|
|
557
|
-
},
|
|
558
|
-
});
|
|
559
|
-
// ── Header / footer / positioned object ──────────────
|
|
560
|
-
rl.registerAction("document.createHeader", {
|
|
561
|
-
description: "Create a DEFAULT header attached to a SectionBreak.",
|
|
562
|
-
inputSchema: {
|
|
563
|
-
document: { type: "string", required: true },
|
|
564
|
-
locationKind: { type: "string", required: false },
|
|
565
|
-
index: { type: "number", required: false },
|
|
566
|
-
segmentId: { type: "string", required: false },
|
|
567
|
-
},
|
|
568
|
-
async execute(input, ctx) {
|
|
569
|
-
const p = (input ?? {});
|
|
570
|
-
const documentId = extractDocumentId(p.document);
|
|
571
|
-
const kind = p.locationKind ?? "location";
|
|
572
|
-
const seg = p.segmentId && p.segmentId !== "body" ? p.segmentId : "";
|
|
573
|
-
const sectionBreakLocation = { segmentId: seg };
|
|
574
|
-
if (kind === "location") {
|
|
575
|
-
if (p.index === undefined) {
|
|
576
|
-
throw new Error("googleDocs: `index` is required when locationKind=location");
|
|
577
|
-
}
|
|
578
|
-
sectionBreakLocation.index = p.index;
|
|
579
|
-
}
|
|
580
|
-
return runBatchUpdate(ctx, documentId, {
|
|
581
|
-
createHeader: { type: "DEFAULT", sectionBreakLocation },
|
|
582
|
-
});
|
|
583
|
-
},
|
|
584
|
-
});
|
|
585
|
-
rl.registerAction("document.deleteHeader", {
|
|
586
|
-
description: "Delete a header by ID.",
|
|
587
|
-
inputSchema: {
|
|
588
|
-
document: { type: "string", required: true },
|
|
589
|
-
headerId: { type: "string", required: true },
|
|
590
|
-
},
|
|
591
|
-
async execute(input, ctx) {
|
|
592
|
-
const p = (input ?? {});
|
|
593
|
-
const documentId = extractDocumentId(p.document);
|
|
594
|
-
return runBatchUpdate(ctx, documentId, { deleteHeader: { headerId: p.headerId } });
|
|
595
|
-
},
|
|
596
|
-
});
|
|
597
|
-
rl.registerAction("document.createFooter", {
|
|
598
|
-
description: "Create a DEFAULT footer attached to a SectionBreak.",
|
|
599
|
-
inputSchema: {
|
|
600
|
-
document: { type: "string", required: true },
|
|
601
|
-
locationKind: { type: "string", required: false },
|
|
602
|
-
index: { type: "number", required: false },
|
|
603
|
-
segmentId: { type: "string", required: false },
|
|
604
|
-
},
|
|
605
|
-
async execute(input, ctx) {
|
|
606
|
-
const p = (input ?? {});
|
|
607
|
-
const documentId = extractDocumentId(p.document);
|
|
608
|
-
const kind = p.locationKind ?? "location";
|
|
609
|
-
const seg = p.segmentId && p.segmentId !== "body" ? p.segmentId : "";
|
|
610
|
-
const sectionBreakLocation = { segmentId: seg };
|
|
611
|
-
if (kind === "location") {
|
|
612
|
-
if (p.index === undefined) {
|
|
613
|
-
throw new Error("googleDocs: `index` is required when locationKind=location");
|
|
614
|
-
}
|
|
615
|
-
sectionBreakLocation.index = p.index;
|
|
616
|
-
}
|
|
617
|
-
return runBatchUpdate(ctx, documentId, {
|
|
618
|
-
createFooter: { type: "DEFAULT", sectionBreakLocation },
|
|
619
|
-
});
|
|
620
|
-
},
|
|
621
|
-
});
|
|
622
|
-
rl.registerAction("document.deleteFooter", {
|
|
623
|
-
description: "Delete a footer by ID.",
|
|
624
|
-
inputSchema: {
|
|
625
|
-
document: { type: "string", required: true },
|
|
626
|
-
footerId: { type: "string", required: true },
|
|
627
|
-
},
|
|
628
|
-
async execute(input, ctx) {
|
|
629
|
-
const p = (input ?? {});
|
|
630
|
-
const documentId = extractDocumentId(p.document);
|
|
631
|
-
return runBatchUpdate(ctx, documentId, { deleteFooter: { footerId: p.footerId } });
|
|
632
|
-
},
|
|
633
|
-
});
|
|
634
|
-
rl.registerAction("document.deletePositionedObject", {
|
|
635
|
-
description: "Delete a positioned object (inline image, floating image, etc.) by its objectId.",
|
|
636
|
-
inputSchema: {
|
|
637
|
-
document: { type: "string", required: true },
|
|
638
|
-
objectId: { type: "string", required: true },
|
|
639
|
-
},
|
|
640
|
-
async execute(input, ctx) {
|
|
641
|
-
const p = (input ?? {});
|
|
642
|
-
const documentId = extractDocumentId(p.document);
|
|
643
|
-
return runBatchUpdate(ctx, documentId, {
|
|
644
|
-
deletePositionedObject: { objectId: p.objectId },
|
|
645
|
-
});
|
|
646
|
-
},
|
|
647
|
-
});
|
|
648
|
-
// ─── Discrete text / paragraph / table formatting ────────────────
|
|
649
|
-
//
|
|
650
|
-
// These wrap individual batchUpdate sub-requests so the agent gets a
|
|
651
|
-
// discoverable surface for the common formatting moves instead of having
|
|
652
|
-
// to assemble a full batchUpdate payload.
|
|
653
|
-
rl.registerAction("document.updateTextStyle", {
|
|
654
|
-
description: "Apply text styling (bold, italic, underline, color, fontSize, fontFamily, link) to a range. Pass `fields` listing which TextStyle properties were set.",
|
|
655
|
-
inputSchema: {
|
|
656
|
-
document: { type: "string", required: true },
|
|
657
|
-
startIndex: { type: "number", required: true },
|
|
658
|
-
endIndex: { type: "number", required: true },
|
|
659
|
-
bold: { type: "boolean", required: false },
|
|
660
|
-
italic: { type: "boolean", required: false },
|
|
661
|
-
underline: { type: "boolean", required: false },
|
|
662
|
-
strikethrough: { type: "boolean", required: false },
|
|
663
|
-
fontSizePt: { type: "number", required: false, description: "Font size in points." },
|
|
664
|
-
fontFamily: { type: "string", required: false },
|
|
665
|
-
foregroundColorHex: { type: "string", required: false, description: "Hex color, e.g. #1A73E8" },
|
|
666
|
-
backgroundColorHex: { type: "string", required: false },
|
|
667
|
-
link: { type: "string", required: false, description: "URL for the linked range." },
|
|
668
|
-
segmentId: { type: "string", required: false, description: "Header/footer/footnote id; omit for the body." },
|
|
669
|
-
},
|
|
670
|
-
async execute(input, ctx) {
|
|
671
|
-
const p = (input ?? {});
|
|
672
|
-
const documentId = extractDocumentId(p.document);
|
|
673
|
-
const ts = {};
|
|
674
|
-
const fields = [];
|
|
675
|
-
if (p.bold !== undefined) {
|
|
676
|
-
ts.bold = p.bold;
|
|
677
|
-
fields.push("bold");
|
|
678
|
-
}
|
|
679
|
-
if (p.italic !== undefined) {
|
|
680
|
-
ts.italic = p.italic;
|
|
681
|
-
fields.push("italic");
|
|
682
|
-
}
|
|
683
|
-
if (p.underline !== undefined) {
|
|
684
|
-
ts.underline = p.underline;
|
|
685
|
-
fields.push("underline");
|
|
686
|
-
}
|
|
687
|
-
if (p.strikethrough !== undefined) {
|
|
688
|
-
ts.strikethrough = p.strikethrough;
|
|
689
|
-
fields.push("strikethrough");
|
|
690
|
-
}
|
|
691
|
-
if (p.fontSizePt !== undefined) {
|
|
692
|
-
ts.fontSize = { magnitude: p.fontSizePt, unit: "PT" };
|
|
693
|
-
fields.push("fontSize");
|
|
694
|
-
}
|
|
695
|
-
if (p.fontFamily) {
|
|
696
|
-
ts.weightedFontFamily = { fontFamily: p.fontFamily };
|
|
697
|
-
fields.push("weightedFontFamily");
|
|
698
|
-
}
|
|
699
|
-
if (p.foregroundColorHex) {
|
|
700
|
-
const c = hexToRgbF(p.foregroundColorHex);
|
|
701
|
-
ts.foregroundColor = { color: { rgbColor: c } };
|
|
702
|
-
fields.push("foregroundColor");
|
|
703
|
-
}
|
|
704
|
-
if (p.backgroundColorHex) {
|
|
705
|
-
const c = hexToRgbF(p.backgroundColorHex);
|
|
706
|
-
ts.backgroundColor = { color: { rgbColor: c } };
|
|
707
|
-
fields.push("backgroundColor");
|
|
708
|
-
}
|
|
709
|
-
if (p.link) {
|
|
710
|
-
ts.link = { url: p.link };
|
|
711
|
-
fields.push("link");
|
|
712
|
-
}
|
|
713
|
-
if (fields.length === 0) {
|
|
714
|
-
throw new Error("googleDocs.document.updateTextStyle: at least one styling property required");
|
|
715
|
-
}
|
|
716
|
-
return runBatchUpdate(ctx, documentId, [
|
|
717
|
-
{
|
|
718
|
-
updateTextStyle: {
|
|
719
|
-
range: {
|
|
720
|
-
startIndex: p.startIndex,
|
|
721
|
-
endIndex: p.endIndex,
|
|
722
|
-
segmentId: p.segmentId,
|
|
723
|
-
},
|
|
724
|
-
textStyle: ts,
|
|
725
|
-
fields: fields.join(","),
|
|
726
|
-
},
|
|
727
|
-
},
|
|
728
|
-
]);
|
|
729
|
-
},
|
|
730
|
-
});
|
|
731
|
-
rl.registerAction("document.updateParagraphStyle", {
|
|
732
|
-
description: "Apply paragraph styling (alignment, named style, indents, spacing, direction) to the paragraphs intersecting the range.",
|
|
733
|
-
inputSchema: {
|
|
734
|
-
document: { type: "string", required: true },
|
|
735
|
-
startIndex: { type: "number", required: true },
|
|
736
|
-
endIndex: { type: "number", required: true },
|
|
737
|
-
alignment: { type: "string", required: false, description: "START | CENTER | END | JUSTIFIED" },
|
|
738
|
-
namedStyleType: { type: "string", required: false, description: "NORMAL_TEXT | TITLE | SUBTITLE | HEADING_1 .. HEADING_6" },
|
|
739
|
-
direction: { type: "string", required: false, description: "LEFT_TO_RIGHT | RIGHT_TO_LEFT" },
|
|
740
|
-
indentFirstLinePt: { type: "number", required: false },
|
|
741
|
-
indentStartPt: { type: "number", required: false },
|
|
742
|
-
indentEndPt: { type: "number", required: false },
|
|
743
|
-
spaceAbovePt: { type: "number", required: false },
|
|
744
|
-
spaceBelowPt: { type: "number", required: false },
|
|
745
|
-
lineSpacing: { type: "number", required: false, description: "Percentage; 100 = single, 150 = 1.5x." },
|
|
746
|
-
segmentId: { type: "string", required: false },
|
|
747
|
-
},
|
|
748
|
-
async execute(input, ctx) {
|
|
749
|
-
const p = (input ?? {});
|
|
750
|
-
const documentId = extractDocumentId(p.document);
|
|
751
|
-
const ps = {};
|
|
752
|
-
const fields = [];
|
|
753
|
-
const pt = (n) => ({ magnitude: n, unit: "PT" });
|
|
754
|
-
if (p.alignment) {
|
|
755
|
-
ps.alignment = p.alignment;
|
|
756
|
-
fields.push("alignment");
|
|
757
|
-
}
|
|
758
|
-
if (p.namedStyleType) {
|
|
759
|
-
ps.namedStyleType = p.namedStyleType;
|
|
760
|
-
fields.push("namedStyleType");
|
|
761
|
-
}
|
|
762
|
-
if (p.direction) {
|
|
763
|
-
ps.direction = p.direction;
|
|
764
|
-
fields.push("direction");
|
|
765
|
-
}
|
|
766
|
-
if (p.indentFirstLinePt !== undefined) {
|
|
767
|
-
ps.indentFirstLine = pt(p.indentFirstLinePt);
|
|
768
|
-
fields.push("indentFirstLine");
|
|
769
|
-
}
|
|
770
|
-
if (p.indentStartPt !== undefined) {
|
|
771
|
-
ps.indentStart = pt(p.indentStartPt);
|
|
772
|
-
fields.push("indentStart");
|
|
773
|
-
}
|
|
774
|
-
if (p.indentEndPt !== undefined) {
|
|
775
|
-
ps.indentEnd = pt(p.indentEndPt);
|
|
776
|
-
fields.push("indentEnd");
|
|
777
|
-
}
|
|
778
|
-
if (p.spaceAbovePt !== undefined) {
|
|
779
|
-
ps.spaceAbove = pt(p.spaceAbovePt);
|
|
780
|
-
fields.push("spaceAbove");
|
|
781
|
-
}
|
|
782
|
-
if (p.spaceBelowPt !== undefined) {
|
|
783
|
-
ps.spaceBelow = pt(p.spaceBelowPt);
|
|
784
|
-
fields.push("spaceBelow");
|
|
785
|
-
}
|
|
786
|
-
if (p.lineSpacing !== undefined) {
|
|
787
|
-
ps.lineSpacing = p.lineSpacing;
|
|
788
|
-
fields.push("lineSpacing");
|
|
789
|
-
}
|
|
790
|
-
if (fields.length === 0) {
|
|
791
|
-
throw new Error("googleDocs.document.updateParagraphStyle: at least one property required");
|
|
792
|
-
}
|
|
793
|
-
return runBatchUpdate(ctx, documentId, [
|
|
794
|
-
{
|
|
795
|
-
updateParagraphStyle: {
|
|
796
|
-
range: { startIndex: p.startIndex, endIndex: p.endIndex, segmentId: p.segmentId },
|
|
797
|
-
paragraphStyle: ps,
|
|
798
|
-
fields: fields.join(","),
|
|
799
|
-
},
|
|
800
|
-
},
|
|
801
|
-
]);
|
|
802
|
-
},
|
|
803
|
-
});
|
|
804
|
-
rl.registerAction("document.updateTableCellStyle", {
|
|
805
|
-
description: "Apply table-cell styling (background color, borders, padding) to a contiguous span of cells. Pass either a single cell via `tableStartLocation+rowIndex+columnIndex`, or a range via `tableStartLocation+rowSpan+columnSpan`.",
|
|
806
|
-
inputSchema: {
|
|
807
|
-
document: { type: "string", required: true },
|
|
808
|
-
tableStartIndex: { type: "number", required: true, description: "The startIndex of the table element." },
|
|
809
|
-
rowIndex: { type: "number", required: true },
|
|
810
|
-
columnIndex: { type: "number", required: true },
|
|
811
|
-
rowSpan: { type: "number", required: false, default: 1 },
|
|
812
|
-
columnSpan: { type: "number", required: false, default: 1 },
|
|
813
|
-
backgroundColorHex: { type: "string", required: false },
|
|
814
|
-
paddingLeftPt: { type: "number", required: false },
|
|
815
|
-
paddingRightPt: { type: "number", required: false },
|
|
816
|
-
paddingTopPt: { type: "number", required: false },
|
|
817
|
-
paddingBottomPt: { type: "number", required: false },
|
|
818
|
-
contentAlignment: { type: "string", required: false, description: "TOP | MIDDLE | BOTTOM" },
|
|
819
|
-
},
|
|
820
|
-
async execute(input, ctx) {
|
|
821
|
-
const p = (input ?? {});
|
|
822
|
-
const documentId = extractDocumentId(p.document);
|
|
823
|
-
const style = {};
|
|
824
|
-
const fields = [];
|
|
825
|
-
const pt = (n) => ({ magnitude: n, unit: "PT" });
|
|
826
|
-
if (p.backgroundColorHex) {
|
|
827
|
-
style.backgroundColor = { color: { rgbColor: hexToRgbF(p.backgroundColorHex) } };
|
|
828
|
-
fields.push("backgroundColor");
|
|
829
|
-
}
|
|
830
|
-
if (p.paddingLeftPt !== undefined) {
|
|
831
|
-
style.paddingLeft = pt(p.paddingLeftPt);
|
|
832
|
-
fields.push("paddingLeft");
|
|
833
|
-
}
|
|
834
|
-
if (p.paddingRightPt !== undefined) {
|
|
835
|
-
style.paddingRight = pt(p.paddingRightPt);
|
|
836
|
-
fields.push("paddingRight");
|
|
837
|
-
}
|
|
838
|
-
if (p.paddingTopPt !== undefined) {
|
|
839
|
-
style.paddingTop = pt(p.paddingTopPt);
|
|
840
|
-
fields.push("paddingTop");
|
|
841
|
-
}
|
|
842
|
-
if (p.paddingBottomPt !== undefined) {
|
|
843
|
-
style.paddingBottom = pt(p.paddingBottomPt);
|
|
844
|
-
fields.push("paddingBottom");
|
|
845
|
-
}
|
|
846
|
-
if (p.contentAlignment) {
|
|
847
|
-
style.contentAlignment = p.contentAlignment;
|
|
848
|
-
fields.push("contentAlignment");
|
|
849
|
-
}
|
|
850
|
-
if (fields.length === 0) {
|
|
851
|
-
throw new Error("googleDocs.document.updateTableCellStyle: at least one style property required");
|
|
852
|
-
}
|
|
853
|
-
return runBatchUpdate(ctx, documentId, [
|
|
854
|
-
{
|
|
855
|
-
updateTableCellStyle: {
|
|
856
|
-
tableRange: {
|
|
857
|
-
tableCellLocation: {
|
|
858
|
-
tableStartLocation: { index: p.tableStartIndex },
|
|
859
|
-
rowIndex: p.rowIndex,
|
|
860
|
-
columnIndex: p.columnIndex,
|
|
861
|
-
},
|
|
862
|
-
rowSpan: p.rowSpan ?? 1,
|
|
863
|
-
columnSpan: p.columnSpan ?? 1,
|
|
864
|
-
},
|
|
865
|
-
tableCellStyle: style,
|
|
866
|
-
fields: fields.join(","),
|
|
867
|
-
},
|
|
868
|
-
},
|
|
869
|
-
]);
|
|
870
|
-
},
|
|
871
|
-
});
|
|
872
|
-
rl.registerAction("document.mergeTableCells", {
|
|
873
|
-
description: "Merge a contiguous block of cells in a table.",
|
|
874
|
-
inputSchema: {
|
|
875
|
-
document: { type: "string", required: true },
|
|
876
|
-
tableStartIndex: { type: "number", required: true },
|
|
877
|
-
rowIndex: { type: "number", required: true },
|
|
878
|
-
columnIndex: { type: "number", required: true },
|
|
879
|
-
rowSpan: { type: "number", required: true },
|
|
880
|
-
columnSpan: { type: "number", required: true },
|
|
881
|
-
},
|
|
882
|
-
async execute(input, ctx) {
|
|
883
|
-
const p = (input ?? {});
|
|
884
|
-
const documentId = extractDocumentId(p.document);
|
|
885
|
-
return runBatchUpdate(ctx, documentId, [
|
|
886
|
-
{
|
|
887
|
-
mergeTableCells: {
|
|
888
|
-
tableRange: {
|
|
889
|
-
tableCellLocation: {
|
|
890
|
-
tableStartLocation: { index: p.tableStartIndex },
|
|
891
|
-
rowIndex: p.rowIndex,
|
|
892
|
-
columnIndex: p.columnIndex,
|
|
893
|
-
},
|
|
894
|
-
rowSpan: p.rowSpan,
|
|
895
|
-
columnSpan: p.columnSpan,
|
|
896
|
-
},
|
|
897
|
-
},
|
|
898
|
-
},
|
|
899
|
-
]);
|
|
900
|
-
},
|
|
901
|
-
});
|
|
902
|
-
rl.registerAction("document.unmergeTableCells", {
|
|
903
|
-
description: "Unmerge a previously merged block of cells.",
|
|
904
|
-
inputSchema: {
|
|
905
|
-
document: { type: "string", required: true },
|
|
906
|
-
tableStartIndex: { type: "number", required: true },
|
|
907
|
-
rowIndex: { type: "number", required: true },
|
|
908
|
-
columnIndex: { type: "number", required: true },
|
|
909
|
-
rowSpan: { type: "number", required: true },
|
|
910
|
-
columnSpan: { type: "number", required: true },
|
|
911
|
-
},
|
|
912
|
-
async execute(input, ctx) {
|
|
913
|
-
const p = (input ?? {});
|
|
914
|
-
const documentId = extractDocumentId(p.document);
|
|
915
|
-
return runBatchUpdate(ctx, documentId, [
|
|
916
|
-
{
|
|
917
|
-
unmergeTableCells: {
|
|
918
|
-
tableRange: {
|
|
919
|
-
tableCellLocation: {
|
|
920
|
-
tableStartLocation: { index: p.tableStartIndex },
|
|
921
|
-
rowIndex: p.rowIndex,
|
|
922
|
-
columnIndex: p.columnIndex,
|
|
923
|
-
},
|
|
924
|
-
rowSpan: p.rowSpan,
|
|
925
|
-
columnSpan: p.columnSpan,
|
|
926
|
-
},
|
|
927
|
-
},
|
|
928
|
-
},
|
|
929
|
-
]);
|
|
930
|
-
},
|
|
931
|
-
});
|
|
932
|
-
rl.registerAction("document.insertInlineImage", {
|
|
933
|
-
description: "Insert an inline image at the given location. `uri` must point to a publicly fetchable image.",
|
|
934
|
-
inputSchema: {
|
|
935
|
-
document: { type: "string", required: true },
|
|
936
|
-
index: { type: "number", required: true },
|
|
937
|
-
uri: { type: "string", required: true },
|
|
938
|
-
widthPt: { type: "number", required: false },
|
|
939
|
-
heightPt: { type: "number", required: false },
|
|
940
|
-
segmentId: { type: "string", required: false },
|
|
941
|
-
},
|
|
942
|
-
async execute(input, ctx) {
|
|
943
|
-
const p = (input ?? {});
|
|
944
|
-
const documentId = extractDocumentId(p.document);
|
|
945
|
-
const pt = (n) => ({ magnitude: n, unit: "PT" });
|
|
946
|
-
const req = {
|
|
947
|
-
location: buildLocation(p.index, p.segmentId),
|
|
948
|
-
uri: p.uri,
|
|
949
|
-
};
|
|
950
|
-
if (p.widthPt !== undefined || p.heightPt !== undefined) {
|
|
951
|
-
req.objectSize = {};
|
|
952
|
-
if (p.widthPt !== undefined)
|
|
953
|
-
req.objectSize.width = pt(p.widthPt);
|
|
954
|
-
if (p.heightPt !== undefined)
|
|
955
|
-
req.objectSize.height = pt(p.heightPt);
|
|
956
|
-
}
|
|
957
|
-
return runBatchUpdate(ctx, documentId, [{ insertInlineImage: req }]);
|
|
958
|
-
},
|
|
959
|
-
});
|
|
960
|
-
rl.registerAction("document.replaceImage", {
|
|
961
|
-
description: "Replace an existing image (identified by its inline-object id) with a new image from a publicly fetchable URI.",
|
|
962
|
-
inputSchema: {
|
|
963
|
-
document: { type: "string", required: true },
|
|
964
|
-
imageObjectId: { type: "string", required: true },
|
|
965
|
-
uri: { type: "string", required: true },
|
|
966
|
-
imageReplaceMethod: { type: "string", required: false, description: "CENTER_CROP (default) | (others as Docs API adds them)" },
|
|
967
|
-
},
|
|
968
|
-
async execute(input, ctx) {
|
|
969
|
-
const p = (input ?? {});
|
|
970
|
-
const documentId = extractDocumentId(p.document);
|
|
971
|
-
return runBatchUpdate(ctx, documentId, [
|
|
972
|
-
{
|
|
973
|
-
replaceImage: {
|
|
974
|
-
imageObjectId: p.imageObjectId,
|
|
975
|
-
uri: p.uri,
|
|
976
|
-
imageReplaceMethod: p.imageReplaceMethod ?? "CENTER_CROP",
|
|
977
|
-
},
|
|
978
|
-
},
|
|
979
|
-
]);
|
|
980
|
-
},
|
|
981
|
-
});
|
|
982
|
-
rl.registerAction("document.insertSectionBreak", {
|
|
983
|
-
description: "Insert a section break at the given location.",
|
|
984
|
-
inputSchema: {
|
|
985
|
-
document: { type: "string", required: true },
|
|
986
|
-
index: { type: "number", required: true },
|
|
987
|
-
sectionType: { type: "string", required: false, description: "CONTINUOUS | NEXT_PAGE. Default CONTINUOUS." },
|
|
988
|
-
segmentId: { type: "string", required: false },
|
|
989
|
-
},
|
|
990
|
-
async execute(input, ctx) {
|
|
991
|
-
const p = (input ?? {});
|
|
992
|
-
const documentId = extractDocumentId(p.document);
|
|
993
|
-
return runBatchUpdate(ctx, documentId, [
|
|
994
|
-
{
|
|
995
|
-
insertSectionBreak: {
|
|
996
|
-
location: buildLocation(p.index, p.segmentId),
|
|
997
|
-
sectionType: p.sectionType ?? "CONTINUOUS",
|
|
998
|
-
},
|
|
999
|
-
},
|
|
1000
|
-
]);
|
|
1001
|
-
},
|
|
1002
|
-
});
|
|
1003
|
-
rl.registerAction("document.updateDocumentStyle", {
|
|
1004
|
-
description: "Update document-level style (page size, margins, page numbers, default direction).",
|
|
1005
|
-
inputSchema: {
|
|
1006
|
-
document: { type: "string", required: true },
|
|
1007
|
-
pageMarginTopPt: { type: "number", required: false },
|
|
1008
|
-
pageMarginBottomPt: { type: "number", required: false },
|
|
1009
|
-
pageMarginLeftPt: { type: "number", required: false },
|
|
1010
|
-
pageMarginRightPt: { type: "number", required: false },
|
|
1011
|
-
pageSizeWidthPt: { type: "number", required: false },
|
|
1012
|
-
pageSizeHeightPt: { type: "number", required: false },
|
|
1013
|
-
useCustomHeaderFooterMargins: { type: "boolean", required: false },
|
|
1014
|
-
},
|
|
1015
|
-
async execute(input, ctx) {
|
|
1016
|
-
const p = (input ?? {});
|
|
1017
|
-
const documentId = extractDocumentId(p.document);
|
|
1018
|
-
const ds = {};
|
|
1019
|
-
const fields = [];
|
|
1020
|
-
const pt = (n) => ({ magnitude: n, unit: "PT" });
|
|
1021
|
-
if (p.pageMarginTopPt !== undefined) {
|
|
1022
|
-
ds.marginTop = pt(p.pageMarginTopPt);
|
|
1023
|
-
fields.push("marginTop");
|
|
1024
|
-
}
|
|
1025
|
-
if (p.pageMarginBottomPt !== undefined) {
|
|
1026
|
-
ds.marginBottom = pt(p.pageMarginBottomPt);
|
|
1027
|
-
fields.push("marginBottom");
|
|
1028
|
-
}
|
|
1029
|
-
if (p.pageMarginLeftPt !== undefined) {
|
|
1030
|
-
ds.marginLeft = pt(p.pageMarginLeftPt);
|
|
1031
|
-
fields.push("marginLeft");
|
|
1032
|
-
}
|
|
1033
|
-
if (p.pageMarginRightPt !== undefined) {
|
|
1034
|
-
ds.marginRight = pt(p.pageMarginRightPt);
|
|
1035
|
-
fields.push("marginRight");
|
|
1036
|
-
}
|
|
1037
|
-
if (p.pageSizeWidthPt !== undefined || p.pageSizeHeightPt !== undefined) {
|
|
1038
|
-
ds.pageSize = {};
|
|
1039
|
-
if (p.pageSizeWidthPt !== undefined)
|
|
1040
|
-
ds.pageSize.width = pt(p.pageSizeWidthPt);
|
|
1041
|
-
if (p.pageSizeHeightPt !== undefined)
|
|
1042
|
-
ds.pageSize.height = pt(p.pageSizeHeightPt);
|
|
1043
|
-
fields.push("pageSize");
|
|
1044
|
-
}
|
|
1045
|
-
if (p.useCustomHeaderFooterMargins !== undefined) {
|
|
1046
|
-
ds.useCustomHeaderFooterMargins = p.useCustomHeaderFooterMargins;
|
|
1047
|
-
fields.push("useCustomHeaderFooterMargins");
|
|
1048
|
-
}
|
|
1049
|
-
if (fields.length === 0) {
|
|
1050
|
-
throw new Error("googleDocs.document.updateDocumentStyle: pass at least one property");
|
|
1051
|
-
}
|
|
1052
|
-
return runBatchUpdate(ctx, documentId, [{ updateDocumentStyle: { documentStyle: ds, fields: fields.join(",") } }]);
|
|
1053
|
-
},
|
|
1054
|
-
});
|
|
1055
|
-
// ─── Small helper for Docs additions ────────────────────────────
|
|
1056
|
-
// (declared at the bottom so it doesn't conflict with the
|
|
1057
|
-
// upstream module-scope `hexToRgb` if one is added later)
|
|
88
|
+
registerDocumentsActions(rl);
|
|
89
|
+
registerTextActions(rl);
|
|
90
|
+
registerTablesActions(rl);
|
|
91
|
+
registerTabActions(rl);
|
|
92
|
+
registerStructureActions(rl);
|
|
93
|
+
registerFormattingActions(rl);
|
|
94
|
+
registerImagesActions(rl);
|
|
1058
95
|
}
|