Skip to content

Google Calander - #16

Open
nihaltp wants to merge 13 commits into
mainfrom
gcal
Open

nihaltp wants to merge 13 commits into
mainfrom
gcal

Conversation

@nihaltp

@nihaltp nihaltp commented Aug 31, 2026 •

Copy link
Copy Markdown
Owner

Summary by Sourcery

Add a Google Calendar userscript for rendering multi-day events in the hourly grid and automate its build and support workflow.

New Features:

  • Add a Google Calendar userscript that displays multi-day events within the hourly grid with accurate day and time spans.
  • Add installation and usage documentation for the Google Calendar Multi-Day Grid Viewer.

Enhancements:

  • Add configurable handling for original multi-day event displays and efficient calendar DOM updates.

Build:

  • Add a dedicated Google Calendar userscript build and generated distribution artifact.

CI:

  • Add automated Google Calendar builds and distribution updates on relevant changes.

Documentation:

  • Add Google Calendar issue templates for general and multi-day grid bug reports.
  • Document the Google Calendar userscript in the project and feature-specific READMEs.

@sourcery-ai

sourcery-ai Bot commented Aug 31, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

Adds and publishes a Google Calendar Multi-Day Grid Viewer userscript that observes Calendar’s DOM, derives multi-day event timing and placement, renders clickable hourly-grid clones, and rebuilds efficiently; the PR also adds its build automation, documentation, and issue templates.

Sequence diagram for Google Calendar multi-day event rendering

sequenceDiagram
    participant Calendar as Google_Calendar_DOM
    participant Observer as MutationObserver
    participant Processor as processCalendar
    participant Grid as Hourly_Grid

    Calendar->>Observer: DOM mutation
    Observer->>Processor: processCalendar()
    Processor->>Processor: buildFingerprint()
    alt structural change detected
        Processor->>Processor: extractTimes()
        Processor->>Grid: append gcal-multiday-clone
        Processor->>Grid: copy event delegation attributes
    else no structural change
        Processor-->>Observer: skip rebuild
    end
Loading

File-Level Changes

Change Details Files
Adds a Google Calendar userscript that projects multi-day all-day chips into each corresponding hourly grid column.
  • Parses event labels to derive start and end times.
  • Calculates day-column placement and pixel offsets from grid dimensions.
  • Creates styled clickable clones using native event attributes and classes.
  • Avoids redundant rebuilds with structural fingerprints and debounced DOM observation.
  • Optionally dims original multi-day chips through a localStorage-backed setting.
google_calendar/index.js
google_calendar/core/dom.js
google_calendar/core/config.js
google_calendar/core/logging.js
google_calendar/core/observer.js
google_calendar/dist/gcal-multiday-grid.user.js
Introduces a dedicated esbuild userscript build and generated distribution artifact.
  • Generates userscript metadata from build configuration and version data.
  • Bundles and formats the Google Calendar entry point for browser installation.
  • Adds an npm build command and workflow that rebuilds and commits the dist output on relevant changes.
google_calendar/build.js
google_calendar/versions.json
google_calendar/dist/gcal-multiday-grid.user.js
package.json
.github/workflows/google-calendar.yml
Documents the new Google Calendar script and adds targeted issue intake.
  • Adds installation and feature documentation to the repository and script directory.
  • Adds general and Multi-Day Grid Viewer bug-report forms.
README.md
google_calendar/README.md
.github/ISSUE_TEMPLATE/google_calendar.yml
.github/ISSUE_TEMPLATE/google_calendar_multiday_grid.yml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Aug 31, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: 5dec63bb-eb4e-4fd5-8778-a4ddabd0ef90


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@mergify

mergify Bot commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

This pull request does not currently match the merge queue conditions, so it cannot be queued from here. The box comes back if it matches again.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 5 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="google_calendar/core/observer.js" line_range="19" />
<code_context>
+
+export function setupObserver() {
+  log('Setting up MutationObserver...');
+  const observer = new MutationObserver(() => {
+    if (debounceTimer) clearTimeout(debounceTimer);
+    debounceTimer = setTimeout(() => {
+      // Pause observing while we mutate the DOM ourselves
+      observer.disconnect();
+      processCalendar();
+      // Resume observing
+      observer.observe(document.body, { childList: true, subtree: true });
+    }, 500);
+  });
+
+  observer.observe(document.body, { childList: true, subtree: true });
+}
</code_context>
<issue_to_address>
**issue (bug_risk):** setupObserver only registers a MutationObserver and never calls processCalendar initially, so a calendar that is already settled when the userscript runs at document-idle gets no clones until an unrelated DOM mutation occurs.

**Triggers:** When Google Calendar finishes rendering before the userscript installs the observer and performs no subsequent child-list mutation.

**Suggested fix:** Call processCalendar once during setup, then install the observer for later changes.

```suggestion
  processCalendar();
  observer.observe(document.body, { childList: true, subtree: true });
```
</issue_to_address>

### Comment 2
<location path="google_calendar/core/dom.js" line_range="144" />
<code_context>
+
+  // Only rebuild clones when the calendar actually changed (navigation, event add/remove).
+  // Skip rebuilds caused by clicks, popovers, hover effects, etc.
+  const fingerprint = buildFingerprint(allDayChips, gridColumns);
+  if (fingerprint === lastFingerprint && document.querySelector('.gcal-multiday-clone')) {
+    log('Skipping rebuild — no structural change detected.');
+    return;
+  }
+  lastFingerprint = fingerprint;
</code_context>
<issue_to_address>
**issue (broader_impact):** The fingerprint contains only column date keys and event IDs, so layout changes that preserve the same calendar structure leave existing clones at their old positions, heights, and text offsets. Browser resizing, zoom changes, or Google Calendar layout recalculation therefore displays stale clone geometry.

**Triggers:** When the calendar is resized or its column dimensions change without changing date keys or event IDs.

**Suggested fix:** Include the relevant column/chip geometry in the rebuild trigger or use a ResizeObserver to recalculate clones after layout changes.

```suggestion
  const fingerprint = [
    buildFingerprint(allDayChips, gridColumns),
    Array.from(gridColumns).map(column => {
      const rect = column.getBoundingClientRect();
      return `${rect.left},${rect.top},${rect.width},${rect.height}`;
    }).join(';'),
    Array.from(allDayChips)
      .filter(chip => !chip.classList.contains('gcal-multiday-clone'))
      .map(chip => {
        const rect = chip.getBoundingClientRect();
        return `${rect.left},${rect.top},${rect.width},${rect.height}`;
      })
      .join(';')
  ].join('::');
```
</issue_to_address>

### Comment 3
<location path="google_calendar/core/dom.js" line_range="176-179" />
<code_context>
+    if (!chip.style.width || !chip.style.width.includes('%')) return;
+
+    const widthPct = parseFloat(chip.style.width) || 14.28;
+    const daysSpan = Math.round(widthPct / 14.28);
+    const leftPct = parseFloat(chip.style.left) || 0;
+    const startDayIndex = Math.round(leftPct / 14.28);
+
+    if (daysSpan > 1) {
</code_context>
<issue_to_address>
**issue (bug_risk):** daysSpan and startDayIndex are calculated using a hard-coded 14.28% per day, so non-seven-day views such as Google Calendar's five-day work-week view calculate incorrect spans and column indices. Multi-day events are consequently skipped or rendered in the wrong columns.

**Triggers:** When the user uses a five-day, four-day, or another non-seven-column calendar view.

**Suggested fix:** Derive the per-day width from the actual grid column count or from the chip and column geometry instead of assuming seven equal columns.

```suggestion
    const dayWidthPct = 100 / gridColumns.length;
    const widthPct = parseFloat(chip.style.width) || dayWidthPct;
    const daysSpan = Math.round(widthPct / dayWidthPct);
    const leftPct = parseFloat(chip.style.left) || 0;
    const startDayIndex = Math.round(leftPct / dayWidthPct);
```
</issue_to_address>

### Comment 4
<location path="google_calendar/core/dom.js" line_range="181-183" />
<code_context>
+    const leftPct = parseFloat(chip.style.left) || 0;
+    const startDayIndex = Math.round(leftPct / 14.28);
+
+    if (daysSpan > 1) {
+      if (config.hideOriginals) {
+        chip.style.opacity = '0.4';
+      }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** The code sets a multi-day chip's opacity to 0.4 when hideOriginals is enabled but never restores the original opacity when the setting is later disabled or the event no longer qualifies as multi-day, leaving the original event permanently dimmed for the rest of the page lifetime.

**Triggers:** When gcal-hide-original is changed from true to false, or an existing event changes from multi-day to a non-multi-day event.

**Suggested fix:** Explicitly restore the chip's original or default opacity when hideOriginals is false and when a chip is no longer converted.
</issue_to_address>

### Comment 5
<location path=".github/workflows/google-calendar.yml" line_range="22" />
<code_context>
+  build:
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+
+    steps:
</code_context>
<issue_to_address>
**🚨 issue (security):** The pull_request workflow executes repository-controlled installation/build commands while requesting contents: write, so a pull request can modify package lifecycle scripts or the build command and run arbitrary code with a write-capable GITHUB_TOKEN.

**Triggers:** When an untrusted contributor can submit a pull request branch in the repository.

**Suggested fix:** Run pull-request builds with read-only permissions and make the dist commit/push a separate push-only job; do not execute untrusted pull-request code with write access.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 5 findings to address first, and the new pull-request workflow grants contents: write while executing repository-controlled npm and build code, so a malicious or mistaken change could push arbitrary repository modifications or expose the write-capable token; those effects are not fully undone by reverting this PR. The userscript itself is ordinary reversible UI behavior, but this CI trust-boundary change needs human review.

Blocking findings: google_calendar/core/observer.js:19, google_calendar/core/dom.js:144, google_calendar/core/dom.js:179, google_calendar/core/dom.js:183, .github/workflows/google-calendar.yml:22


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread google_calendar/core/observer.js Resolved
Comment thread google_calendar/core/dom.js Resolved
Comment on lines +176 to +179
const widthPct = parseFloat(chip.style.width) || 14.28;
const daysSpan = Math.round(widthPct / 14.28);
const leftPct = parseFloat(chip.style.left) || 0;
const startDayIndex = Math.round(leftPct / 14.28);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): daysSpan and startDayIndex are calculated using a hard-coded 14.28% per day, so non-seven-day views such as Google Calendar's five-day work-week view calculate incorrect spans and column indices. Multi-day events are consequently skipped or rendered in the wrong columns.

Triggers: When the user uses a five-day, four-day, or another non-seven-column calendar view.

Suggested fix: Derive the per-day width from the actual grid column count or from the chip and column geometry instead of assuming seven equal columns.

Suggested change
const widthPct = parseFloat(chip.style.width) || 14.28;
const daysSpan = Math.round(widthPct / 14.28);
const leftPct = parseFloat(chip.style.left) || 0;
const startDayIndex = Math.round(leftPct / 14.28);
const dayWidthPct = 100 / gridColumns.length;
const widthPct = parseFloat(chip.style.width) || dayWidthPct;
const daysSpan = Math.round(widthPct / dayWidthPct);
const leftPct = parseFloat(chip.style.left) || 0;
const startDayIndex = Math.round(leftPct / dayWidthPct);

Comment on lines +181 to +183
if (daysSpan > 1) {
if (config.hideOriginals) {
chip.style.opacity = '0.4';

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (bug_risk): The code sets a multi-day chip's opacity to 0.4 when hideOriginals is enabled but never restores the original opacity when the setting is later disabled or the event no longer qualifies as multi-day, leaving the original event permanently dimmed for the rest of the page lifetime.

Triggers: When gcal-hide-original is changed from true to false, or an existing event changes from multi-day to a non-multi-day event.

Suggested fix: Explicitly restore the chip's original or default opacity when hideOriginals is false and when a chip is no longer converted.

build:
runs-on: ubuntu-latest
permissions:
contents: write

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚨 issue (security): The pull_request workflow executes repository-controlled installation/build commands while requesting contents: write, so a pull request can modify package lifecycle scripts or the build command and run arbitrary code with a write-capable GITHUB_TOKEN.

Triggers: When an untrusted contributor can submit a pull request branch in the repository.

Suggested fix: Run pull-request builds with read-only permissions and make the dist commit/push a separate push-only job; do not execute untrusted pull-request code with write access.

nihaltp and others added 6 commits August 31, 2026 23:28
@mergify

mergify Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

👋 nihaltp your PR is conflicting and needs to be updated to be merged

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant