From 0430d4954982a2e91412fc22c79f89a8b06ef181 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 08:01:19 +0000 Subject: [PATCH 1/4] Add CODE_REVIEW.md and LIGHTNING_TALK.md Co-authored-by: lucasrangit <701818+lucasrangit@users.noreply.github.com> --- CODE_REVIEW.md | 40 ++++++++++++++++++++++++++++++++++++ LIGHTNING_TALK.md | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 CODE_REVIEW.md create mode 100644 LIGHTNING_TALK.md diff --git a/CODE_REVIEW.md b/CODE_REVIEW.md new file mode 100644 index 0000000..b54af85 --- /dev/null +++ b/CODE_REVIEW.md @@ -0,0 +1,40 @@ +# Code Review: Plotline Google Docs Add-on + +## Summary +The codebase is logically organized into frontend (`Sidebar.html`, `PickerModal.html`) and backend components (`Code.js`, `Data.js`, `Library.js`). The separation of Google Docs context vs standard Apps Script logic is solid. However, following the `AGENTS.md` guidelines and general best practices, the following architectural and stylistic improvements would elevate the project. + +## 1. Modern JavaScript & V8 Engine Support + +Google Apps Script now runs on the V8 engine, which supports ES2017 syntax. Several files use older syntax that can be modernized for readability and performance. + +### Suggestions: +- **`var` vs `let`/`const`:** In frontend scripts (e.g., `Sidebar.html` and `PickerModal.html`), variables are extensively declared using `var` (e.g., `var html`, `var appData`). Use `let` and `const` for proper block-scoping. +- **Loops:** You rely heavily on C-style `for` loops (e.g., `for (let i = 0; i < paragraphs.length; i++)` in `Data.js`). Utilize `for...of` loops, or higher-order array methods like `.map()`, `.filter()`, and `.reduce()` for cleaner iteration. +- **Object Iteration:** In `Data.js` (`migrateRevisionLegacyCachedWordCounts`, `getRevisionCachedWordCounts`), loops are driven by `Object.keys()`. You can streamline these blocks using `Object.entries()` or modern iteration directly on Maps if you refactor the caching logic. +- **String Interpolation:** Update string concatenations (e.g., `"Revision " + rev.id + " Date: " + new Date(rev.date).toLocaleString()`) to template literals (e.g., `` `Revision ${rev.id} Date: ${new Date(rev.date).toLocaleString()}` ``). + +## 2. Magic Strings and Centralized Constants + +Strings used for property keys, DOM IDs, and error messages are hardcoded across multiple functions, which increases the likelihood of typos and complicates future refactors. + +### Suggestions: +- Extract property keys (e.g., `'WORD_COUNT_GOAL'`, `'SIMULATE_FILE_NOT_FOUND'`) into a central `Constants` object at the top of your scripts. +- Extract common error string matchers in `showError()` (`Sidebar.html`) to well-named constant arrays or objects. +- Centralize cache keys and prefixes (e.g., `"REV_WC_"`, `"ALL_REVISIONS_CACHE"`) into the same module/scope to prevent drift. + +## 3. Frontend Architecture (Separation of Concerns) + +The HTML files (`Sidebar.html`, `PickerModal.html`) contain a dense mixture of HTML markup, inline CSS styles, and client-side JavaScript logic. + +### Suggestions: +- **Componentization:** Move styles into a separate `Stylesheet.html` and scripts into a `JavaScript.html` file. You can then include these dynamically in your main HTML files using `HtmlService.createTemplateFromFile()` and `` scriptlets. +- **Inline Event Handlers:** Remove inline HTML event handlers (e.g., `onload="onLoad()"`, `onclick="refreshOutline()"`) in favor of standard event listeners attached within your client-side JavaScript block (e.g., `document.getElementById('refreshBtn').addEventListener('click', refreshOutline)`). +- **DOM Manipulations:** The frequent string concatenation to build HTML nodes (e.g., `var html = '
`Date,WordCount`). The next time the user opens the sidebar or refreshes, Plotline pulls everything from the cache instantly, and only queries the Drive API for *new* revisions that happened since the last check. It dramatically speeds up the UI." + +--- + +## 4:30 - UI Implementation & Wrap Up (30 seconds) + +**Presenter:** +"Finally, the UI itself. We use `HtmlService` to serve the sidebar, and client-side JavaScript talks to our backend functions using `google.script.run`. Because Apps Script calls are asynchronous, we use success and failure handlers to update the DOM—like rendering our Chart.js timeline or showing error messages—without freezing the Docs interface. + +And that's Plotline! A serverless add-on turning raw revision data into actionable insights for writers. Thanks!" From 57d0f7fdfeceb991495263ab90c09d1857955073 Mon Sep 17 00:00:00 2001 From: Lucas Rangit MAGASWERAN Date: Tue, 28 Jul 2026 10:36:20 +0200 Subject: [PATCH 2/4] Update CODE_REVIEW.md --- CODE_REVIEW.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CODE_REVIEW.md b/CODE_REVIEW.md index b54af85..dbe63c5 100644 --- a/CODE_REVIEW.md +++ b/CODE_REVIEW.md @@ -11,7 +11,6 @@ Google Apps Script now runs on the V8 engine, which supports ES2017 syntax. Seve - **`var` vs `let`/`const`:** In frontend scripts (e.g., `Sidebar.html` and `PickerModal.html`), variables are extensively declared using `var` (e.g., `var html`, `var appData`). Use `let` and `const` for proper block-scoping. - **Loops:** You rely heavily on C-style `for` loops (e.g., `for (let i = 0; i < paragraphs.length; i++)` in `Data.js`). Utilize `for...of` loops, or higher-order array methods like `.map()`, `.filter()`, and `.reduce()` for cleaner iteration. - **Object Iteration:** In `Data.js` (`migrateRevisionLegacyCachedWordCounts`, `getRevisionCachedWordCounts`), loops are driven by `Object.keys()`. You can streamline these blocks using `Object.entries()` or modern iteration directly on Maps if you refactor the caching logic. -- **String Interpolation:** Update string concatenations (e.g., `"Revision " + rev.id + " Date: " + new Date(rev.date).toLocaleString()`) to template literals (e.g., `` `Revision ${rev.id} Date: ${new Date(rev.date).toLocaleString()}` ``). ## 2. Magic Strings and Centralized Constants From 45919c0e3f7dd79a4c494f23cc5643cb464a102d Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 29 Jul 2026 07:27:52 +0000 Subject: [PATCH 3/4] Update CODE_REVIEW.md with best practices Co-authored-by: lucasrangit <701818+lucasrangit@users.noreply.github.com> --- CODE_REVIEW.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/CODE_REVIEW.md b/CODE_REVIEW.md index dbe63c5..ca750dc 100644 --- a/CODE_REVIEW.md +++ b/CODE_REVIEW.md @@ -11,6 +11,7 @@ Google Apps Script now runs on the V8 engine, which supports ES2017 syntax. Seve - **`var` vs `let`/`const`:** In frontend scripts (e.g., `Sidebar.html` and `PickerModal.html`), variables are extensively declared using `var` (e.g., `var html`, `var appData`). Use `let` and `const` for proper block-scoping. - **Loops:** You rely heavily on C-style `for` loops (e.g., `for (let i = 0; i < paragraphs.length; i++)` in `Data.js`). Utilize `for...of` loops, or higher-order array methods like `.map()`, `.filter()`, and `.reduce()` for cleaner iteration. - **Object Iteration:** In `Data.js` (`migrateRevisionLegacyCachedWordCounts`, `getRevisionCachedWordCounts`), loops are driven by `Object.keys()`. You can streamline these blocks using `Object.entries()` or modern iteration directly on Maps if you refactor the caching logic. +- **String Interpolation:** Update string concatenations (e.g., `"Revision " + rev.id + " Date: " + new Date(rev.date).toLocaleString()`) to template literals (e.g., `` `Revision ${rev.id} Date: ${new Date(rev.date).toLocaleString()}` ``). ## 2. Magic Strings and Centralized Constants @@ -30,10 +31,13 @@ The HTML files (`Sidebar.html`, `PickerModal.html`) contain a dense mixture of H - **Inline Event Handlers:** Remove inline HTML event handlers (e.g., `onload="onLoad()"`, `onclick="refreshOutline()"`) in favor of standard event listeners attached within your client-side JavaScript block (e.g., `document.getElementById('refreshBtn').addEventListener('click', refreshOutline)`). - **DOM Manipulations:** The frequent string concatenation to build HTML nodes (e.g., `var html = '
Date: Wed, 29 Jul 2026 08:37:35 +0000 Subject: [PATCH 4/4] Add production monitoring suggestion to CODE_REVIEW.md Co-authored-by: lucasrangit <701818+lucasrangit@users.noreply.github.com> --- CODE_REVIEW.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CODE_REVIEW.md b/CODE_REVIEW.md index ca750dc..f7ff7ee 100644 --- a/CODE_REVIEW.md +++ b/CODE_REVIEW.md @@ -38,6 +38,7 @@ The HTML files (`Sidebar.html`, `PickerModal.html`) contain a dense mixture of H - **Configuration & Secrets:** Configuration options in `PickerModal.html` refer directly to `GOOGLE_CLOUD_API_KEY` and `GOOGLE_CLOUD_PROJECT_NUMBER`. Ensure proper fallback defaults and validation in your backend configuration getters. Avoid checking any real keys into the codebase. - **Error Handling & Quotas:** The backoff retry wrapper in `Library.js` is excellent for handling 429s. However, ensure you properly wait explicitly on `429` retry-after headers if they are provided by the Drive API, rather than purely relying on exponential backoff, to better respect Google's quota limits. - **Manifest Permissions (`appsscript.json`):** You have defined narrow scopes like `documents.currentonly` which is great for security. Ensure that the Stackdriver exception logging (`"exceptionLogging": "STACKDRIVER"`) does not inadvertently log PII from document text when an error is thrown. +- **Production Monitoring:** Since the add-on is distributed to end users, set up robust monitoring using Google Cloud operations suite (formerly Stackdriver) or a third-party service. Actively track error rates (especially `429` rate limits and permissions errors), API latency, and quota exhaustion to proactively address performance regressions or scale issues before users report them. --- *Overall, the integration logic is sound and the backend is well-structured for Apps Script execution.*