Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/chrome-extension/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
dist/
*.zip
.DS_Store
85 changes: 85 additions & 0 deletions apps/chrome-extension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Cap Chrome Extension

A Chrome extension that launches Cap's web-based recorder for instant screen, tab, and camera recording.

## Features

- **Quick Access**: One-click launcher to open Cap's web recorder
- **Smart Tab Management**: Reuses existing recorder tabs instead of creating duplicates
- **Full Recording Suite**: Access to all Cap recording features via the web app:
- Multiple recording modes (screen, window, tab, camera)
- Recording controls (start/stop, pause/resume)
- Device selection (camera and microphone)
- Real-time recording timer
- Automatic upload to Cap

## Installation

### From Source

1. Install dependencies:
```bash
pnpm install
```

2. Build the extension:
```bash
pnpm build
```

3. Load in Chrome:
- Open Chrome and navigate to `chrome://extensions/`
- Enable "Developer mode" (toggle in top right)
- Click "Load unpacked"
- Select the `dist` folder


## Usage

### First Time Setup

1. Click the Cap extension icon in your browser toolbar
2. Click "Sign In to Cap" to authenticate
3. You'll be redirected to Cap's authentication page
4. Once authenticated, you're ready to record!

### Recording

1. Click the Cap extension icon
2. Select your recording mode (Screen, Tab, or Camera)
3. Configure audio options:
- Enable/disable microphone
- Enable/disable camera overlay
4. Click "Start Recording"
5. Grant necessary permissions when prompted
6. Record your content
7. Click "Stop" when finished
8. Your recording will automatically upload to Cap


### Adding New Features

1. Update the appropriate component file
2. If adding new permissions, update `manifest.json`
3. Test thoroughly in development mode
4. Build and test the production version

## Browser Compatibility

- Chrome 100+
- Edge 100+
- Other Chromium-based browsers

## Permissions

The extension requires the following permissions:

- **`tabs`**: Access tab information for tab recording

## Contributing

Contributions are welcome! Please follow the existing code style and test thoroughly before submitting PRs.

## License

See the main Cap repository for license information.
27 changes: 27 additions & 0 deletions apps/chrome-extension/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"manifest_version": 3,
"name": "Cap - Screen Recorder",
"version": "1.0.0",
"description": "Record your screen, tab, or camera instantly with Cap",
"permissions": [
"tabs"
],
"background": {
"service_worker": "src/background/service-worker.js",
"type": "module"
},
"action": {
"default_icon": {
"16": "src/assets/icons/icon-16.png",
"32": "src/assets/icons/icon-32.png",
"48": "src/assets/icons/icon-48.png",
"128": "src/assets/icons/icon-128.png"
}
},
"icons": {
"16": "src/assets/icons/icon-16.png",
"32": "src/assets/icons/icon-32.png",
"48": "src/assets/icons/icon-48.png",
"128": "src/assets/icons/icon-128.png"
}
}
15 changes: 15 additions & 0 deletions apps/chrome-extension/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "@cap/chrome-extension",
"version": "1.0.0",
"type": "module",
"description": "Cap Chrome Extension - Screen recorder for the web",
"scripts": {
"build": "node scripts/build.js"
},
"devDependencies": {
"archiver": "^7.0.1",
"chokidar": "^4.0.3",
"esbuild": "^0.24.2",
"sharp": "^0.34.5"
}
}
60 changes: 60 additions & 0 deletions apps/chrome-extension/scripts/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const rootDir = path.resolve(__dirname, '..');
const srcDir = path.join(rootDir, 'src');
const distDir = path.join(rootDir, 'dist');

function cleanDist() {
if (fs.existsSync(distDir)) {
fs.rmSync(distDir, { recursive: true, force: true });
}
fs.mkdirSync(distDir, { recursive: true });
}

function copyDirectory(src, dest) {
if (!fs.existsSync(dest)) {
fs.mkdirSync(dest, { recursive: true });
}

const entries = fs.readdirSync(src, { withFileTypes: true });

for (const entry of entries) {
const srcPath = path.join(src, entry.name);
const destPath = path.join(dest, entry.name);

if (entry.isDirectory()) {
copyDirectory(srcPath, destPath);
} else {
fs.copyFileSync(srcPath, destPath);
}
}
}

async function build() {
try {
console.log('Building extension...');

cleanDist();

fs.copyFileSync(
path.join(rootDir, 'manifest.json'),
path.join(distDir, 'manifest.json')
);
console.log('Copied manifest.json');

copyDirectory(srcDir, path.join(distDir, 'src'));
console.log('Copied src/ directory');

console.log('Build complete!');
} catch (error) {
console.error('Build failed:', error);
process.exit(1);
}
}

build();
Empty file.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apps/chrome-extension/src/assets/icons/icon-16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 18 additions & 0 deletions apps/chrome-extension/src/background/service-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const isDev = !('update_url' in chrome.runtime.getManifest());
const WEB_RECORDER_URL = isDev ? 'http://localhost:3000/record' : 'https://cap.so/record';

chrome.runtime.onInstalled.addListener(() => {
console.log('Cap extension installed');
});

chrome.action.onClicked.addListener(async () => {
const tabs = await chrome.tabs.query({ url: WEB_RECORDER_URL });

if (tabs.length > 0) {
const tab = tabs[0];
await chrome.tabs.update(tab.id, { active: true });
await chrome.windows.update(tab.windowId, { focused: true });
} else {
chrome.tabs.create({ url: WEB_RECORDER_URL });
}
});
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const RecordingModeSelector = ({
)}
</SelectValue>
</SelectTrigger>
<SelectContent className="z-[502] max-w-[280px]">
<SelectContent className="z-[502] max-w-[320px]">
{Object.entries(recordingModeOptions).map(([value, option]) => {
const OptionIcon = option.icon;
const isFullscreen = value === "fullscreen";
Expand Down
6 changes: 4 additions & 2 deletions apps/web/app/(org)/login/form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const MotionButton = motion(Button);
export function LoginForm() {
const searchParams = useSearchParams();
const router = useRouter();
const next = searchParams?.get("next");
const next = searchParams?.get("next") || searchParams?.get("callbackUrl");
const [email, setEmail] = useState("");
const [loading, setLoading] = useState(false);
const [emailSent, setEmailSent] = useState(false);
Expand Down Expand Up @@ -124,7 +124,9 @@ export function LoginForm() {
);
setOrganizationName(data.name);

signIn("workos", undefined, {
signIn("workos", {
...(next && next.length > 0 ? { callbackUrl: next } : {}),
}, {
organization: data.organizationId,
connection: data.connectionId,
});
Expand Down
Loading