Skip to main content

Browser extension

The PlanningSup browser extension provides a lightweight, always-accessible version of the calendar in your browser toolbar.

Installation

The extension is available on the Chrome Web Store:

Install from Chrome Web Store

Compatible with Chrome, Edge, Brave, and other Chromium-based browsers
Firefox support is planned but not yet available. Use the web app or PWA instead.

Features

The extension includes:

Full calendar viewsDay, week, and month views in a popup or side panel

Synced preferencesSettings, colors, and filters sync with the web app

Quick accessClick the toolbar icon to open your calendar instantly

Privacy-focusedNo tracking or analytics in the extension

How it works

The extension is a popup that embeds the PlanningSup web app in an iframe:
// apps/extension/src/manifest.ts
export default defineManifest({
  name: 'PlanningSup',
  version: '1.0.0',
  manifest_version: 3,
  action: {
    default_popup: 'src/popup/index.html',
    default_icon: 'icon-512.png',
  },
  permissions: ['storage'],
  host_permissions: ['https://planningsup.app/*'],
})
The popup (src/popup/index.html) loads the same Vue app as the web version, but with a fixed viewport optimized for the extension popup size.

Authentication in the extension

Since extensions can’t use OAuth redirects directly, PlanningSup uses a message-passing flow:
1

User clicks 'Sign in with Discord'

The extension opens the OAuth URL in a new tab
2

User authorizes on Discord

Discord redirects to https://planningsup.app/auth-callback?code=...
3

Web page sends message to extension

The callback page uses chrome.runtime.sendMessage() to notify the extension
4

Extension completes authentication

The extension background script exchanges the code for a session cookie
// apps/web/src/composables/useAuth.ts
if (IS_EXTENSION) {
  const { default: browser } = await import('webextension-polyfill')
  browser.runtime.onMessage.addListener(async (message: any) => {
    if (message.type !== 'authCallback') return
    
    const callbackUrl = new URL(`${BACKEND_URL}/api/auth/callback/${message.provider}`)
    callbackUrl.searchParams.set('code', message.code)
    callbackUrl.searchParams.set('state', message.state)
    callbackUrl.searchParams.set('client', 'extension')
    
    await fetch(callbackUrl, { credentials: 'include' })
    window.location.reload()
  })
}
See apps/web/src/composables/useAuth.ts:78-126 for the full implementation.
Passkeys (WebAuthn) are not supported in the extension popup due to browser security restrictions. Use social login instead.

Building the extension

The extension is built using WXT, a modern framework for browser extensions:
cd apps/extension
bun install
bun run build  # Output: .output/chrome-mv3/
The build process:
  1. Compiles Vue components with Vite
  2. Bundles background scripts and content scripts
  3. Copies static assets (icons, manifest)
  4. Generates a .zip file for Chrome Web Store submission

Manifest V3

PlanningSup uses Manifest V3, the latest Chrome extension standard:
// apps/extension/src/manifest.ts
export default defineManifest({
  manifest_version: 3,
  permissions: ['storage'],
  host_permissions: ['https://planningsup.app/*'],
  action: {
    default_popup: 'src/popup/index.html',
  },
  background: {
    service_worker: 'src/background/main.ts',
  },
})
By default, PlanningSup opens as a popup (small window below the toolbar icon). You can also open it in a side panel for a larger view:
1

Right-click the extension icon

In your browser toolbar
2

Select 'Open in side panel'

(Chrome 114+ only)
The side panel gives you more space to view the calendar while browsing other tabs.
If your browser doesn’t support side panels, you can open the extension in a new tab by right-clicking the icon and selecting “Open in new tab.”

Storage and sync

The extension uses chrome.storage.local to persist settings:
import { storage } from 'webextension-polyfill'

// Save a setting
await storage.local.set({ 'settings.colors': JSON.stringify(colors) })

// Load a setting
const { 'settings.colors': colorsJson } = await storage.local.get('settings.colors')
When you sign in, settings sync via the PlanningSup API (same as the web app).

Permissions

The extension requests minimal permissions:
  • storage: To save your settings locally
  • host_permissions for planningsup.app: To communicate with the API
No other permissions are required. The extension does not:
  • Access your browsing history
  • Read or modify web pages
  • Track your activity

Updating the extension

The extension updates automatically via the Chrome Web Store. When a new version is available:
  1. Chrome downloads it in the background
  2. The extension reloads the next time you open it
  3. You may see a “PlanningSup has been updated” notification
Extension updates are independent of the web app. You can have different versions running simultaneously.

Limitations

The extension has some limitations compared to the web app:
  • No passkey support: OAuth only
  • Fixed popup size: Limited to ~400x600px (unless opened in a side panel or tab)
  • No PWA features: No offline mode or install prompt

Privacy

The extension does not:
  • Track your usage
  • Send analytics
  • Access third-party APIs (except PlanningSup and your university’s ICS feed)
All data is stored locally or on the PlanningSup server (when signed in).

Uninstalling

To remove the extension:
1

Open Chrome extensions

Go to chrome://extensions/ or click the puzzle icon → Manage extensions
2

Find PlanningSup

Locate “PlanningSup” in the list
3

Click 'Remove'

Confirm the removal
Your settings remain synced on the server if you were signed in. You can reinstall the extension later without losing data.

Next steps

Desktop & mobile

Install native apps with Tauri

Web app

Learn about the full-featured web version

Build docs developers (and LLMs) love