Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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
102 changes: 102 additions & 0 deletions apps/chrome-extension/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Cap Chrome Extension

A Chrome extension for Cap that enables instant screen, tab, and camera recording directly from your browser.

## Features

- **Multiple Recording Modes**
- Screen recording (entire screen or specific window)
- Tab recording (current browser tab)
- Camera recording (webcam only)

- **Recording Controls**
- Start/stop recording
- Pause/resume during recording
- Real-time recording timer
- On-page recording indicator

- **Audio Options**
- Optional microphone audio
- System audio capture (for tab recording)
- High-quality audio encoding

- **Seamless Integration**
- Automatic upload to Cap
- Direct link to recorded video
- Progress tracking during upload
- Multipart upload for large files
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

logic: README claims the extension provides recording controls, audio options, and upload functionality. The actual extension code only opens a tab to /record - all these features are implemented in the web app, not the extension. Update the README to clarify that the extension is a launcher for the web-based recorder.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/chrome-extension/README.md
Line: 5:27

Comment:
**logic:** README claims the extension provides recording controls, audio options, and upload functionality. The actual extension code only opens a tab to `/record` - all these features are implemented in the web app, not the extension. Update the README to clarify that the extension is a launcher for the web-based recorder.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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


## 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:

- **`storage`**: Store authentication tokens and user preferences
- **`tabs`**: Access tab information for tab recording
- **`activeTab`**: Capture current tab content
- **`scripting`**: Inject content script for recording indicator
- **`offscreen`**: Create offscreen document for media capture
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

logic: README lists permissions that aren't declared in manifest.json. The manifest only requests tabs, but this lists storage, activeTab, scripting, and offscreen. Either add these permissions to the manifest or update the README to match what's actually implemented.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/chrome-extension/README.md
Line: 86:94

Comment:
**logic:** README lists permissions that aren't declared in `manifest.json`. The manifest only requests `tabs`, but this lists `storage`, `activeTab`, `scripting`, and `offscreen`. Either add these permissions to the manifest or update the README to match what's actually implemented.

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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


## 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