Skip to content

Conversation

@PeerRich
Copy link
Member

@PeerRich PeerRich commented Nov 23, 2025

CleanShot.2025-11-23.at.21.44.31.mp4

Summary by cubic

Introduces Cal.com Companion: an Expo app and Chrome extension to create one-off booking links and manage event types, bookings, and availability. Adds a browser sidebar (with Gmail support) to generate and share single-use links fast.

  • New Features
    • One-off booking links for quick, single-use scheduling.
    • Expo app with tabs for Event Types, Bookings, and Availability, plus detail and edit/create flows (limits, advanced, recurring).
    • Chrome MV3 extension with a collapsible sidebar and Gmail compose integration.
    • Typed Cal API v2 client for event types, bookings, schedules, private links, webhooks, and user profiles.
    • Authentication via web session or token, profile header, Expo Router, and NativeWind styling.

Written for commit f138f55. Summary will update automatically on new commits.

@PeerRich PeerRich requested a review from a team as a code owner November 23, 2025 20:44
@graphite-app graphite-app bot requested a review from a team November 23, 2025 20:44
@keithwillcode keithwillcode added core area: core, team members only platform Anything related to our platform plan labels Nov 23, 2025
@vercel
Copy link

vercel bot commented Nov 23, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal Ignored Ignored Nov 23, 2025 9:08pm
cal-eu Ignored Ignored Nov 23, 2025 9:08pm

@PeerRich PeerRich closed this Nov 23, 2025
@dhairyashiil dhairyashiil reopened this Nov 23, 2025
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

36 issues found across 86 files

Prompt for AI agents (all 36 issues)

Understand the root cause of the following 36 issues and fix them.


<file name="companion/dev/.wxt/tsconfig.json">

<violation number="1" location="companion/dev/.wxt/tsconfig.json:12">
`paths` is configured without a `baseUrl`, which causes TypeScript to throw TS5052 and makes this config unusable. Add a `baseUrl` (e.g., the project root) before defining path aliases.</violation>
</file>

<file name="companion/app/(tabs)/_layout.tsx">

<violation number="1" location="companion/app/(tabs)/_layout.tsx:16">
Wrap the NativeTabs labels in t() (or equivalent) so these tab names are localized instead of hard-coded strings.</violation>

<violation number="2" location="companion/app/(tabs)/_layout.tsx:56">
Use t() (or equivalent) for the Tabs.Screen titles so the tab names are localized rather than hard-coded strings.</violation>
</file>

<file name="companion/.wxt/tsconfig.json">

<violation number="1" location="companion/.wxt/tsconfig.json:12">
Define compilerOptions.baseUrl before declaring paths so the alias map is honored; otherwise these path imports will not resolve in TypeScript.</violation>
</file>

<file name="companion/extension/public/manifest.json">

<violation number="1" location="companion/extension/public/manifest.json:6">
Host permissions must be declared in the `host_permissions` field for MV3; keeping `http://localhost:8081/*` inside `permissions` makes the manifest invalid and the extension will fail to load.</violation>
</file>

<file name="companion/app/event-type-detail/tabs/AvailabilityTab.tsx">

<violation number="1" location="companion/app/event-type-detail/tabs/AvailabilityTab.tsx:28">
Wrap user-facing text in `t()` (with the appropriate translation keys) instead of hard-coded strings so the UI participates in the localization pipeline.</violation>
</file>

<file name="companion/app/event-type-detail/tabs/BasicsTab.tsx">

<violation number="1" location="companion/app/event-type-detail/tabs/BasicsTab.tsx:47">
Wrap every user-visible label, placeholder, and message in t() instead of hard-coded strings so the component can be localized.</violation>
</file>

<file name="companion/app/event-type-detail/tabs/AdvancedTab.tsx">

<violation number="1" location="companion/app/event-type-detail/tabs/AdvancedTab.tsx:76">
Wrap user-facing copy in t() instead of hard-coded strings so the screen is localizable; every label/placeholder in this component currently bypasses localization.</violation>
</file>

<file name="companion/babel.config.js">

<violation number="1" location="companion/babel.config.js:6">
`nativewind/babel` should be configured as a plugin rather than a preset; otherwise NativeWind’s transformer never runs and className styles won’t compile.</violation>
</file>

<file name="companion/components/SvgImage.tsx">

<violation number="1" location="companion/components/SvgImage.tsx:9">
`SvgImageProps.style` is typed as only `{ marginRight?: number }`, so consumers cannot pass normal React Native styles or StyleSheet references even though the prop is forwarded to `&lt;View&gt;`. Widen the type to accept standard view styles.</violation>
</file>

<file name="companion/app/event-type-detail/tabs/RecurringTab.tsx">

<violation number="1" location="companion/app/event-type-detail/tabs/RecurringTab.tsx:32">
Wrap user-facing text in `t()` instead of hardcoding strings so the recurring settings UI can be localized.</violation>
</file>

<file name="companion/app/event-type-detail/constants.ts">

<violation number="1" location="companion/app/event-type-detail/constants.ts:4">
All option labels in these new constants (�No buffer time�, �5 Minutes�, tab labels, etc.) are hardcoded English strings, so the Event Type detail UI cannot be localized; wrap each label with t() (or provide translation keys) instead of embedding raw text.</violation>
</file>

<file name="companion/extension/entrypoints/content.ts">

<violation number="1" location="companion/extension/entrypoints/content.ts:46">
The sidebar iframe points to http://localhost:8081, which is blocked on HTTPS pages and unavailable in production, so the companion UI will never load. Please point the iframe to a packaged or HTTPS-served resource.</violation>

<violation number="2" location="companion/extension/entrypoints/content.ts:439">
Event-type fields are interpolated into `menuItem.innerHTML` without escaping, so untrusted titles/descriptions can inject arbitrary HTML/JS into Gmail when the menu renders. Please build the DOM with `textContent`/`createElement` or sanitize the strings before insertion to prevent XSS.</violation>
</file>

<file name="companion/components/Header.tsx">

<violation number="1" location="companion/components/Header.tsx:124">
The Sign Out flow never clears auth state; after confirming, it only logs to the console, leaving the user signed in. Wire this button to the real sign-out logic so the session is terminated.</violation>

<violation number="2" location="companion/components/Header.tsx:221">
User-visible labels such as “My Profile” are hard-coded instead of using `t()` for localization, violating the project’s localization guideline. Please route these strings through the translation helper before rendering.</violation>
</file>

<file name="companion/services/webAuth.ts">

<violation number="1" location="companion/services/webAuth.ts:23">
Splitting cookie strings on every `=` truncates values (e.g., NextAuth tokens lose their padding), so `getCookies()` returns corrupted cookie values.</violation>
</file>

<file name="companion/app/event-type-detail/tabs/LimitsTab.tsx">

<violation number="1" location="companion/app/event-type-detail/tabs/LimitsTab.tsx:86">
Wrap user-visible copy in `t()` (and import the translation helper) instead of hard-coding English strings like “Before event” so the UI can be localized.</violation>
</file>

<file name="companion/services/calcom.ts">

<violation number="1" location="companion/services/calcom.ts:24">
A Cal API key is bundled into the client (EXPO_PUBLIC), exposing privileged credentials to every user. Server API keys must never ship in frontend bundles—proxy requests through a backend or require per-user tokens instead.</violation>

<violation number="2" location="companion/services/calcom.ts:219">
Thrown API errors omit the HTTP status code, so callers like getEventTypeById cannot tell a 404 from other failures and always throw instead of returning null. Include the response status when raising the error.</violation>

<violation number="3" location="companion/services/calcom.ts:399">
Rule violated: **Avoid Logging Sensitive Information**

`getBookingByUid` logs entire booking payloads (user/host/attendee objects) straight to the console, exposing attendee emails and other PII in clear text. Remove these logs or replace them with sanitized summaries to comply with the &quot;Avoid Logging Sensitive Information&quot; rule.</violation>
</file>

<file name="companion/utils/defaultLocations.ts">

<violation number="1" location="companion/utils/defaultLocations.ts:34">
Wrap user-facing label/placeholder/message strings in the `t()` localization helper (or export translation keys) so these default locations can be translated.</violation>
</file>

<file name="companion/app/event-type-detail/utils.ts">

<violation number="1" location="companion/app/event-type-detail/utils.ts:50">
Unhandled default locations now return null, so selecting the &quot;Somewhere else&quot; option (type `somewhereElse`/`attendeeDefined`) no longer produces a location value and the location input breaks.</violation>
</file>

<file name="companion/app/(tabs)/bookings.tsx">

<violation number="1" location="companion/app/(tabs)/bookings.tsx:549">
`Alert.prompt` is iOS-only, so the new cancellation flow silently fails on Android. Guard the call and provide an Android-compatible input method before invoking `cancelBooking`.</violation>
</file>

<file name="companion/app/(tabs)/availability.tsx">

<violation number="1" location="companion/app/(tabs)/availability.tsx:51">
Refreshing schedules discards the active search filter. After the user types a query, any new fetch resets `filteredSchedules` to the full list while `searchQuery` still holds the query, so the list and input get out of sync.</violation>

<violation number="2" location="companion/app/(tabs)/availability.tsx:278">
User-facing text such as the search placeholder is hardcoded instead of using `t()`, so this screen cannot be localized per project guidelines.</violation>
</file>

<file name="companion/contexts/AuthContext.tsx">

<violation number="1" location="companion/contexts/AuthContext.tsx:54">
Store the fetched profile in `userInfo` when recovering a web token so context consumers receive user data.</violation>
</file>

<file name="companion/app/event-type-detail.tsx">

<violation number="1" location="companion/app/event-type-detail.tsx:71">
Hooking up the multi-duration UI states to `handleSave` is missing, so enabling multiple durations or changing their defaults has no effect on the saved event type.</violation>

<violation number="2" location="companion/app/event-type-detail.tsx:488">
Skip fetching event-type data when the route id is non-numeric (e.g., &quot;new&quot;); otherwise the create screen makes a failing API call with id NaN as soon as it mounts.</violation>

<violation number="3" location="companion/app/event-type-detail.tsx:1240">
Wrap the header/CTA text (e.g., &quot;Create Event Type&quot;, &quot;Create&quot;, &quot;Save&quot;) in `t()` so these user-facing strings can be localized instead of remaining hard-coded English.</violation>
</file>

<file name="companion/wxt.config.js">

<violation number="1" location="companion/wxt.config.js:12">
Move the origin patterns (localhost + api.cal.com) out of `permissions` and into a `host_permissions` entry; `permissions` must contain only API permissions in Manifest V3 or the extension will be rejected.</violation>
</file>

<file name="companion/dev/entrypoints/content.ts">

<violation number="1" location="companion/dev/entrypoints/content.ts:41">
Sidebar iframe always points to http://localhost:8081, so nothing loads for real users</violation>

<violation number="2" location="companion/dev/entrypoints/content.ts:81">
Wrap the toggle button tooltip in t() so it can be localized instead of hardcoding English text.</violation>

<violation number="3" location="companion/dev/entrypoints/content.ts:101">
Use t() when setting the close button tooltip rather than embedding the English string directly so the text can be localized.</violation>
</file>

<file name="companion/app/availability-detail.tsx">

<violation number="1" location="companion/app/availability-detail.tsx:474">
Wrap user-facing strings with the `t()` localization helper (and import/use the translation hook) instead of hardcoding English copy.</violation>
</file>

<file name="companion/app/booking-detail.tsx">

<violation number="1" location="companion/app/booking-detail.tsx:190">
Rule violated: **Avoid Logging Sensitive Information**

Remove the debug logging of the booking/user/attendee objects; it violates the &quot;Avoid Logging Sensitive Information&quot; rule by printing PII to the console.</violation>
</file>

Reply to cubic to teach it or ask questions. Re-run a review with @cubic-dev-ai review this PR

"resolveJsonModule": true,
"strict": true,
"skipLibCheck": true,
"paths": {
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 23, 2025

Choose a reason for hiding this comment

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

paths is configured without a baseUrl, which causes TypeScript to throw TS5052 and makes this config unusable. Add a baseUrl (e.g., the project root) before defining path aliases.

Prompt for AI agents
Address the following comment on companion/dev/.wxt/tsconfig.json at line 12:

<comment>`paths` is configured without a `baseUrl`, which causes TypeScript to throw TS5052 and makes this config unusable. Add a `baseUrl` (e.g., the project root) before defining path aliases.</comment>

<file context>
@@ -0,0 +1,28 @@
+    &quot;resolveJsonModule&quot;: true,
+    &quot;strict&quot;: true,
+    &quot;skipLibCheck&quot;: true,
+    &quot;paths&quot;: {
+      &quot;@&quot;: [&quot;..&quot;],
+      &quot;@/*&quot;: [&quot;../*&quot;],
</file context>

✅ Addressed in 3dba738

<Tabs.Screen
name="event-types"
options={{
title: "Event Types",
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 23, 2025

Choose a reason for hiding this comment

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

Use t() (or equivalent) for the Tabs.Screen titles so the tab names are localized rather than hard-coded strings.

Prompt for AI agents
Address the following comment on companion/app/(tabs)/_layout.tsx at line 56:

<comment>Use t() (or equivalent) for the Tabs.Screen titles so the tab names are localized rather than hard-coded strings.</comment>

<file context>
@@ -0,0 +1,98 @@
+      &lt;Tabs.Screen
+        name=&quot;event-types&quot;
+        options={{
+          title: &quot;Event Types&quot;,
+          tabBarIcon: ({ color, focused }) =&gt; (
+            &lt;Ionicons name={focused ? &quot;link&quot; : &quot;link-outline&quot;} size={24} color={color} /&gt;
</file context>
Fix with Cubic

<NativeTabs>
<NativeTabs.Trigger name="event-types">
<Icon sf="link" />
<Label>Event Types</Label>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 23, 2025

Choose a reason for hiding this comment

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

Wrap the NativeTabs labels in t() (or equivalent) so these tab names are localized instead of hard-coded strings.

Prompt for AI agents
Address the following comment on companion/app/(tabs)/_layout.tsx at line 16:

<comment>Wrap the NativeTabs labels in t() (or equivalent) so these tab names are localized instead of hard-coded strings.</comment>

<file context>
@@ -0,0 +1,98 @@
+      &lt;NativeTabs&gt;
+        &lt;NativeTabs.Trigger name=&quot;event-types&quot;&gt;
+          &lt;Icon sf=&quot;link&quot; /&gt;
+          &lt;Label&gt;Event Types&lt;/Label&gt;
+        &lt;/NativeTabs.Trigger&gt;
+
</file context>
Fix with Cubic

"resolveJsonModule": true,
"strict": true,
"skipLibCheck": true,
"paths": {
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 23, 2025

Choose a reason for hiding this comment

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

Define compilerOptions.baseUrl before declaring paths so the alias map is honored; otherwise these path imports will not resolve in TypeScript.

Prompt for AI agents
Address the following comment on companion/.wxt/tsconfig.json at line 12:

<comment>Define compilerOptions.baseUrl before declaring paths so the alias map is honored; otherwise these path imports will not resolve in TypeScript.</comment>

<file context>
@@ -0,0 +1,28 @@
+    &quot;resolveJsonModule&quot;: true,
+    &quot;strict&quot;: true,
+    &quot;skipLibCheck&quot;: true,
+    &quot;paths&quot;: {
+      &quot;@&quot;: [&quot;../extension&quot;],
+      &quot;@/*&quot;: [&quot;../extension/*&quot;],
</file context>

✅ Addressed in 3dba738

"name": "Cal.com Companion",
"version": "1.7.0",
"description": "Your calendar companion for quick booking and scheduling",
"permissions": ["activeTab", "http://localhost:8081/*"],
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 23, 2025

Choose a reason for hiding this comment

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

Host permissions must be declared in the host_permissions field for MV3; keeping http://localhost:8081/* inside permissions makes the manifest invalid and the extension will fail to load.

Prompt for AI agents
Address the following comment on companion/extension/public/manifest.json at line 6:

<comment>Host permissions must be declared in the `host_permissions` field for MV3; keeping `http://localhost:8081/*` inside `permissions` makes the manifest invalid and the extension will fail to load.</comment>

<file context>
@@ -0,0 +1,20 @@
+  &quot;name&quot;: &quot;Cal.com Companion&quot;,
+  &quot;version&quot;: &quot;1.7.0&quot;,
+  &quot;description&quot;: &quot;Your calendar companion for quick booking and scheduling&quot;,
+  &quot;permissions&quot;: [&quot;activeTab&quot;, &quot;http://localhost:8081/*&quot;],
+  &quot;content_security_policy&quot;: {
+    &quot;extension_pages&quot;: &quot;script-src &#39;self&#39;; object-src &#39;self&#39;; frame-src &#39;self&#39; http://localhost:8081;&quot;
</file context>
Fix with Cubic

toggleButton.style.fontSize = '16px';
toggleButton.style.boxShadow = '0 2px 8px rgba(0,0,0,0.2)';
toggleButton.style.transition = 'all 0.2s ease';
toggleButton.title = 'Toggle sidebar';
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 23, 2025

Choose a reason for hiding this comment

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

Wrap the toggle button tooltip in t() so it can be localized instead of hardcoding English text.

Prompt for AI agents
Address the following comment on companion/dev/entrypoints/content.ts at line 81:

<comment>Wrap the toggle button tooltip in t() so it can be localized instead of hardcoding English text.</comment>

<file context>
@@ -0,0 +1,197 @@
+    toggleButton.style.fontSize = &#39;16px&#39;;
+    toggleButton.style.boxShadow = &#39;0 2px 8px rgba(0,0,0,0.2)&#39;;
+    toggleButton.style.transition = &#39;all 0.2s ease&#39;;
+    toggleButton.title = &#39;Toggle sidebar&#39;;
+
+    // Create close button
</file context>

✅ Addressed in 3dba738

options={{
headerShown: true,
headerTitle: () => (
<Text style={{ fontSize: 20, fontWeight: "700" }}>Edit Availability</Text>
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 23, 2025

Choose a reason for hiding this comment

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

Wrap user-facing strings with the t() localization helper (and import/use the translation hook) instead of hardcoding English copy.

Prompt for AI agents
Address the following comment on companion/app/availability-detail.tsx at line 474:

<comment>Wrap user-facing strings with the `t()` localization helper (and import/use the translation hook) instead of hardcoding English copy.</comment>

<file context>
@@ -0,0 +1,926 @@
+        options={{
+          headerShown: true,
+          headerTitle: () =&gt; (
+            &lt;Text style={{ fontSize: 20, fontWeight: &quot;700&quot; }}&gt;Edit Availability&lt;/Text&gt;
+          ),
+          headerLeft: () =&gt; (
</file context>
Fix with Cubic


// Create iframe
const iframe = document.createElement('iframe');
iframe.src = 'http://localhost:8081';
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 23, 2025

Choose a reason for hiding this comment

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

Sidebar iframe always points to http://localhost:8081, so nothing loads for real users

Prompt for AI agents
Address the following comment on companion/dev/entrypoints/content.ts at line 41:

<comment>Sidebar iframe always points to http://localhost:8081, so nothing loads for real users</comment>

<file context>
@@ -0,0 +1,197 @@
+    
+    // Create iframe
+    const iframe = document.createElement(&#39;iframe&#39;);
+    iframe.src = &#39;http://localhost:8081&#39;;
+    iframe.style.width = &#39;250px&#39;;
+    iframe.style.maxWidth = &#39;250px&#39;;
</file context>

✅ Addressed in 3dba738

setLoading(true);
setError(null);
const bookingData = await CalComAPIService.getBookingByUid(uid);
console.log("Booking data received:", JSON.stringify(bookingData, null, 2));
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 23, 2025

Choose a reason for hiding this comment

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

Rule violated: Avoid Logging Sensitive Information

Remove the debug logging of the booking/user/attendee objects; it violates the "Avoid Logging Sensitive Information" rule by printing PII to the console.

Prompt for AI agents
Address the following comment on companion/app/booking-detail.tsx at line 190:

<comment>Remove the debug logging of the booking/user/attendee objects; it violates the &quot;Avoid Logging Sensitive Information&quot; rule by printing PII to the console.</comment>

<file context>
@@ -0,0 +1,726 @@
+      setLoading(true);
+      setError(null);
+      const bookingData = await CalComAPIService.getBookingByUid(uid);
+      console.log(&quot;Booking data received:&quot;, JSON.stringify(bookingData, null, 2));
+      console.log(&quot;User:&quot;, bookingData.user);
+      console.log(&quot;Hosts:&quot;, bookingData.hosts);
</file context>
Fix with Cubic

},
"2024-08-13"
);
console.log("getBookingByUid raw response:", JSON.stringify(response, null, 2));
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot Nov 23, 2025

Choose a reason for hiding this comment

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

Rule violated: Avoid Logging Sensitive Information

getBookingByUid logs entire booking payloads (user/host/attendee objects) straight to the console, exposing attendee emails and other PII in clear text. Remove these logs or replace them with sanitized summaries to comply with the "Avoid Logging Sensitive Information" rule.

Prompt for AI agents
Address the following comment on companion/services/calcom.ts at line 399:

<comment>`getBookingByUid` logs entire booking payloads (user/host/attendee objects) straight to the console, exposing attendee emails and other PII in clear text. Remove these logs or replace them with sanitized summaries to comply with the &quot;Avoid Logging Sensitive Information&quot; rule.</comment>

<file context>
@@ -0,0 +1,1053 @@
+        },
+        &quot;2024-08-13&quot;
+      );
+      console.log(&quot;getBookingByUid raw response:&quot;, JSON.stringify(response, null, 2));
+      if (response &amp;&amp; response.data) {
+        console.log(&quot;getBookingByUid booking data:&quot;, JSON.stringify(response.data, null, 2));
</file context>
Fix with Cubic

@dhairyashiil dhairyashiil changed the base branch from main to feat/expo-wxt-companion November 23, 2025 21:00
Resolved conflicts by preserving logic from both branches:
- event-types.tsx: Kept one-off meeting modal with Clear button from upstream
- _layout.tsx: Combined both inline styles and Tailwind classes
- content.ts: Kept transparent full-width sidebar container with 400px iframe from HEAD, updated to use double quotes from upstream
- Deleted files removed by upstream: .output files, dev/entrypoints/content.ts, wxt.config.js
@dhairyashiil dhairyashiil merged commit a6340da into feat/expo-wxt-companion Nov 23, 2025
13 of 14 checks passed
@dhairyashiil dhairyashiil deleted the expo-one-off-links branch November 23, 2025 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

core area: core, team members only platform Anything related to our platform plan size/S

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants