# notion-clipper-skill

## Summary
Notion Clipper automates saving web pages into Notion by using Chrome DevTools Protocol to fully render JavaScript-driven sites, converting the rendered HTML to Markdown, and importing it as native Notion blocks. Key features include CLI-driven clipping to a specific Notion database or page, auto-installation of Node dependencies on first run, and a recommended execution pattern that unsets proxy env vars and uses the tsx runtime to avoid common network/runtime issues. Use cases include archiving articles, saving research and reference pages, capturing dynamic single-page apps, and centralizing web-based notes for personal or team knowledge management. Core advantages are faithful rendering of dynamic content, structured Notion-native blocks for searchability and editing, secure use via your Notion integration key, and minimal setup for reproducible clipping workflows.

# Notion Clipper
Clip any web page to Notion. Uses Chrome CDP for full JavaScript rendering, converts to Markdown, then to Notion blocks.

## Prerequisites
1. **Notion API Key**: Create an integration at [https://notion.so/my-integrations](https://notion.so/my-integrations)
2. **Store the key**:
   
   ```bash
   mkdir -p ~/.config/notion
   echo "ntn_your_key_here" > ~/.config/notion/api_key
   ```

3. **Share target database/page** with your integration (click "..." → "Connect to" → your integration name)

## First Time Setup
Dependencies are auto-installed when the script runs. No manual setup needed.

## Agent Execution Instructions
**CRITICAL**: Always use the command pattern below. It auto-installs dependencies on first run.
1. Determine this SKILL.md file's directory path as `SKILL_DIR`
2. **Command pattern** (package.json in `scripts/`; always run lazy install first):
   
   ```bash
   (cd "${SKILL_DIR}/scripts" && (test -d node_modules || npm install) && npx -y tsx main.ts <args>)
   ```

3. Replace `${SKILL_DIR}` with the actual path (e.g. `/Users/xxx/.claude/skills/notion-clipper-skill`)

## Usage
**IMPORTANT - Use this command pattern for best results:**
   
   ```bash
   # Recommended: Clear proxy env vars and use tsx runtime
   (cd "${SKILL_DIR}/scripts" && (test -d node_modules || npm install) && unset http_proxy https_proxy all_proxy && npx -y tsx main.ts <url> --database-name "Resources")
   ```

**Why this pattern?**
- `unset http_proxy https_proxy all_proxy` - Avoids ECONNREFUSED from proxy conflicts
- `tsx` runtime - Node.js runtime that properly handles direct connections (bun has proxy issues)
- `(test -d node_modules || npm install)` - Auto-installs dependencies if missing

**If you encounter network issues:**
1. Close any VPN/proxy software
2. Switch to a stable network (mobile hotspot often works)
3. Use the recommended command pattern above

## Options
| Option | Description |
| --- | --- |
| `<url>` | URL to clip |
| `--database-name, -n <name>` | Target database by name (searches for match) |
| `--database, -d <id>` | Target Notion database by ID |
| `--page, -p <id>` | Target Notion page ID (appends blocks) |
| `--list-databases, -l` | List all accessible databases and exit |
| `--wait, -w` | Wait for user signal before capturing |
| `--timeout, -t <ms>` | Page load timeout (default: 30000) |
| `--no-bookmark` | Don't include bookmark block at top |

## Capture Modes
| Mode | Behavior | Use When |
| --- | --- | --- |
| Auto (default) | Capture on network idle | Public pages, static content |
| Wait (`--wait`) | User signals when ready | Login-required, lazy loading, paywalls |

## Output Structure
When saving to a **database**, creates a new page with:
- **Name**: Page title
- **URL**: Source URL (if database has URL property)
- **Content**: Bookmark block + converted content blocks

When appending to a **page**, adds:
- Bookmark block (link to source)
- Divider
- Converted content blocks

## Database Setup
For best results, create a Notion database with these properties:
- **Name** (Title) - Required, will contain page title
- **URL** (URL) - Optional, will contain source URL

## How It Works
1. **Fetch**: Launch Chrome via CDP, navigate to URL
2. **Render**: Wait for JavaScript to execute, scroll to trigger lazy loading
3. **Extract**: Run cleanup script to remove ads/nav, extract main content
4. **Convert**: HTML → Markdown → Notion blocks
5. **Save**: Call Notion API to create page or append blocks

## Dependencies
- Chrome/Chromium browser (installed locally)
- Node.js (script runs with `tsx`; Bun may route through proxy and return empty body, use Node)
- Notion API key configured

## Troubleshooting
### Network Issues
| Error | Cause | Solution |
| --- | --- | --- |
| `ECONNREFUSED 208.103.161.1:443` | DNS returns blocked IP; proxy conflict | 1. Close VPN/proxy software 2. Use `unset http_proxy https_proxy all_proxy` 3. Switch network (e.g., mobile hotspot) |
| `Notion API returned empty body (status 200)` | Using `bun` which routes through proxy incorrectly | Run with **tsx**: `npx -y tsx main.ts ...` (NOT `bun`) |
| `fetch failed` or `ECONNREFUSED` | Proxy env vars set but Node.js `https` doesn't support them | Either: 1. Use network without proxy (unset env vars) 2. Ensure proxy allows HTTPS traffic |

### Content Issues
| Error | Cause | Solution |
| --- | --- | --- |
| `Invalid URL for link` | Notion API rejects non-http(s) URLs | Script now **removes all markdown links** by default to avoid validation errors. Content is preserved, only links are stripped. |

**Note**: The script automatically removes these invalid URL types:
- `javascript:`, `data:`, `file:`, `about:` protocols
- WeChat internal links (`weixin:`, `wx://`)
- Relative paths (`/path`, `./path`)
- Hash-only links (`#anchor`)
- Empty links

### General Issues
- **Chrome not found**: Set `NOTION_CLIPPER_CHROME_PATH` environment variable
- **Timeout errors**: Increase `--timeout` value or use `--wait` mode
- **Content missing**: Try `--wait` mode for dynamic/lazy-loaded pages
- **Notion API error (401/403)**: Check API key validity and integration permissions
- **Notion API error**: Ensure integration has access to target database/page

---
