Skip to content

Repository files navigation

CraftClipper

A Chrome MV3 extension that clips web pages into Craft via the Craft Space API. Content extraction uses Defuddle (the same engine as the Obsidian Web Clipper). YouTube transcript, description, and metadata capture comes from Defuddle's built-in YouTube extractor.

Highlights:

  • Clean Markdown extraction from any page (or just your current text selection).
  • YouTube transcript capture through Defuddle's site-specific extractor.
  • A folder picker sourced from your Craft space, with the last-used folder remembered.
  • An editable title + Markdown preview before anything is saved — what's in the box is exactly what gets sent to Craft.
  • Documents are structured to match the official Craft web clipper's output (title + source/site/author/published callouts, a #clippings tag, a saved-at caption, and a divider before the body).

See DESIGN.md for the full architecture and design rationale.

Install (unpacked, from dist/)

  1. Build the extension (see Development below) so dist/ exists.
  2. Open chrome://extensions in Chrome.
  3. Enable Developer mode (top right).
  4. Click Load unpacked and select the dist/ folder in this repo.
  5. Pin the CraftClipper icon to your toolbar if you'd like quick access.

Setup: connect your Craft space

CraftClipper talks to Craft through a Space API link — a secret URL that is itself the credential (no separate login/API key).

  1. In Craft, open Settings → Connect / API and create a new API connection link for your space.
  2. Copy the link Craft gives you. It looks like https://connect.craft.do/links/<id>/api/v1 (a plain https://connect.craft.do/links/<id> link, or just the bare <id>, also work — CraftClipper normalizes whatever you paste).
  3. Right-click the CraftClipper toolbar icon → Options (or open the popup while unconfigured and click Open settings).
  4. Paste the link into Craft Space API link, click Test connection to confirm it resolves to your space, then Save.

Your link is stored in chrome.storage.sync (so it follows you across Chrome installs signed into the same Google account) and is never sent anywhere except https://connect.craft.do.

Usage

  1. Open the page you want to clip (or select some text on it first, to clip just that selection).
  2. Click the CraftClipper toolbar icon.
  3. Edit the title if you like, pick a destination folder (or leave it on Unsorted), and review/edit the Markdown preview.
  4. Click Save to Craft. On success you'll see a link to open the new document directly in the Craft app.

On a youtube.com/watch page, CraftClipper still uses Defuddle directly. Defuddle's YouTube extractor fetches the video's transcript when captions are available and includes the description and metadata in the extracted content.

Manual smoke test checklist

  • A normal article page clips with a clean Markdown body.
  • Selecting a paragraph before opening the popup clips only that selection.
  • A YouTube video with captions produces a Transcript section.
  • A YouTube video without captions still clips metadata/description.
  • The folder picker lists your space's folders (indented as Parent / Child), remembers your last pick, and its refresh button re-fetches from Craft.
  • An invalid/expired API link produces a readable error (not a silent failure) both when testing the connection in Options and when saving from the popup.
  • chrome:// pages and the Chrome Web Store show a friendly "can't be clipped" message instead of a dead popup.

Development

Requires Node.js v22+ and npm 10+.

npm install       # install typescript, esbuild, @types/chrome, defuddle
npm run build     # bundle content/background/popup/options into dist/, copy static files
npm run watch     # same, but rebuilds on file changes (esbuild incremental context)
npm run typecheck # tsc --noEmit across src/

After npm run build, reload the unpacked extension at chrome://extensions (the reload icon on the CraftClipper card) to pick up changes.

Project layout

src/
  content.ts        # ISOLATED world: extraction orchestrator
  lib/extract.ts     # Defuddle wrapper: parse, selection fragment, html→md, fallbacks
  lib/markdown.ts     # clip payload → Craft markdown (header callouts + body)
  lib/craft.ts        # Craft API client (fetch wrapper, typed endpoints)
  lib/storage.ts      # chrome.storage.sync settings + last-used folder / folder cache
  background.ts      # service worker: save flow, MAIN-world shadow-DOM flatten
  popup/             # popup.html/ts/css — preview + folder picker + save
  options/           # options.html/ts/css — API link setup + connection test
manifest.json        # copied to dist/ by build.mjs
build.mjs            # esbuild-based build script
scripts/gen-icons.mjs # one-off script that generated icons/*.png

Notes / deviations from DESIGN.md

  • createMarkdownContent is imported as a named export from defuddle/full (import { createMarkdownContent } from "defuddle/full") rather than destructured off the default export. At runtime the installed defuddle@0.19 package attaches createMarkdownContent as a static property of the default-exported Defuddle class (so DefuddleFull.createMarkdownContent does work), but its shipped .d.ts only declares it as a named export, so the destructuring-off-default form fails tsc --noEmit. The named import is behaviorally identical and type-checks cleanly.

No image re-hosting

Images are left as remote URLs in the clipped Markdown (Craft's POST /upload requires per-file binary uploads, which is out of scope for v1 — see DESIGN.md "Non-goals").

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages