unDrifter — Living Copy Doc for Figma
Current Version: Bumbler (v0.2.0)
> "Go then, there are other worlds than these."
---
Quick Links:
- Setup Guide — How to run locally
- Agent Context — For AI agents continuing this work
Hosted hub: https://undrifter.com — use your own PUBLIC_URL when self-hosting.
---
What Is unDrifter?
unDrifter extracts structured content from webpages and syncs it to FigJam/Figma as editable text. Think of it as a "Living Copy Doc" — content captured from websites becomes the source of truth for your design system.
Core insight: Don't recreate layouts. Extract the content (headings, paragraphs, lists, quotes, images, links) and let designers work with it natively.
---
Features
Content Capture (Chrome Extension)
- Three capture modes — Select (click to detect container), Draw (drag full-width band), Full Page (scroll-only; loads lazy content, no interaction — captures page as-is, no modals)
- Crop-style zone selection with resize handles (Select/Draw modes)
- Semantic extraction — headings, paragraphs, lists, quotes, images, links; span and div (leaf text) for eyebrow/body/brand
- Smart inference — detects navs, cards, heroes, CTAs from ARIA roles and class names
- Expand Show More (Select/Draw only) — expands accordions and "Show More" within bounds; skips gallery/carousel/tab; full-page does not expand (zero interaction)
- Smart fallback — expands bounds or drills into containers if zone appears empty; includes collapsed body copy
- Screenshot capture — full screenshot of selected region
- Batch mode — capture multiple zones, push all at once
- Empty zone warning — prompts before capturing semantically empty regions
- Zone loading from URLs — automatically loads zones when opening URL from plugin
- Edit zones — rename zones directly in extension UI
- Delete zones — remove zones from extension, syncs to server
CopyDoc in FigJam (Widget)
- Create before design — Add variables in FigJam before any Figma design file exists
- One sticky per variable — Add spawns variable stickies freely on the board; Refresh creates stickies for variables without widgets
- Status workflow — Draft → Review → Approved with color coding; click to cycle; "All approved" indicator
- Link to Figma — Bind FigJam board to Figma design file
- Edit & sync — Edit copy in FigJam stickies; in Figma plugin, click "Sync vars" to update
- Multiplayer — Stakeholders edit copy in shared FigJam boards
Content Management (Figma Plugin)
- Outline, Blank, Links, Settings — main nav (icon above label); Settings holds feature flags (AI, screenshot, CSS tokens, Suggest, Debug)
- Outline view — tree structure of all zones and children; global filter; Search by path (semantic)
- URL grouping — zones organized by source URL with clickable links
- Injection width — select a frame before injecting to use its width so content wraps to match layout
- Delete by URL — remove all zones from a specific URL at once
- Semantic badges — visual indicators for content types
- Paste & Link — paste content to any text layer, stays linked to source
- Component property support — paste to component TEXT properties
- Inspector panel buttons — Open, Sync, Unlink right in Figma's inspector
- Figma Variables — create String Variables from CopyDoc content
- Sync all links — batch update all linked layers from source
- Manual content — add content directly from plugin UI
- Single zone injection — inject individual zones to canvas
- Copy to clipboard — click any content to copy plain text
- Image placement — place images directly on canvas from outline
Data Flow
- Local-first — all data stays on your machine
- Source of truth — zones persist after injection for re-sync
- Cross-plugin compatible — shared plugin data readable by other plugins
- URL-based management — zones grouped and manageable by source URL
- Figma OAuth — optional authentication for future cloud features
- Whitespace normalization — clean text extraction and display
- Accessibility filtering — automatically filters visually hidden a11y text
---
Architecture
┌─────────────────────────────────────────────────────────────────────┐
│ USER'S MACHINE │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ ┌──────────────┐ │
│ │ Chrome Extension │────▶│ Local Server │◀────│ Figma Plugin │ │
│ │ - Zone capture │ POST│ - Data bridge │ GET │ - Node create│ │
│ │ - DOM extract │ │ - Variables API │ │ - Variables │ │
│ │ - Screenshot │ │ - OAuth handler │ │ - Sync vars │ │
│ └──────────────────┘ └────────▲─────────┘ └──────────────┘ │
│ │ │
│ ┌──────────────────┐ │ │
│ │ FigJam Widget │──GET/PATCH───┘ │
│ │ - Create CopyDoc│ (variables, figjam/items) │
│ │ - Edit sticky │ │
│ │ - Link Figma │ │
│ └──────────────────┘ data/copydoc.json │
└─────────────────────────────────────────────────────────────────────┘
Why this architecture? 1. Figma REST API is read-only — you must use Plugin API to create nodes 2. Chrome extension and plugin can't communicate directly — server bridges them 3. Local-first for privacy — user content never leaves their machine
---
Quick Start
From the repo root you can use a single control surface:
bash
1. Clone and install all dependencies
git clone <repo-url> && cd unDrifter
npm run install:all
2. Start server (keep this running)
npm start
3. Build extension, plugin, and widget (once, or after code changes)
npm run build
Then: Load extension/dist in chrome://extensions; link plugin in Figma → Development; import widget in FigJam → Development
Root scripts
| Command | Description |
| --------- | ------------- |
npm start | Start the local server (port 3001) |
npm run stop | Stop the server (kills process on 3001) |
npm run restart | Stop then start the server |
npm run build | Build extension + plugin + widget |
npm run build:widget | Build FigJam widget only |
npm run install:all | Install deps in server, extension, plugin, widget, shared |
See SETUP.md for detailed instructions.
---
Usage
1. Capture Content
1. Go to any webpage 2. Click unDrifter extension icon 3. Choose a capture mode: Select (click to select a container), Draw (drag a full-width band), or Full Page (captures everything; scrolls first to load lazy content) 4. Adjust with crop handles if needed (Select/Draw) 5. Confirm each zone 6. Click "Push to FigJam"
2. Inject to Canvas
1. Open FigJam or Figma 2. Run unDrifter plugin 3. Click "Refresh" to load zones 4. Click "Inject All" to create nodes on canvas
3. Paste to Layers
1. Select any text layer in Figma 2. In plugin, click "Paste & Link" on any content 3. Content is pasted and linked to source 4. Use "Sync" to update from source later
4. Create Variables
1. Click "📊 Vars" button in plugin 2. Creates String Variables in "CopyDoc" collection 3. Bind variables to text layers for native editing
5. CopyDoc in FigJam (Widget)
1. Open FigJam, insert the unDrifter Copy widget (Resources → Widgets → Development) 2. Create from FigJam: Add variables (name optional, value) and click Add — creates CopyDoc before any design exists 3. Or Refresh to load variables from extension captures 4. Edit in sticky notes — blur or Enter to save; server updates 5. Link Figma design file (paste URL/key, click Link Figma) 6. In Figma: unDrifter plugin → Sync vars to pull edits into design 7. Bind text layers to CopyDoc variables to see updates on canvas
6. Manage by URL
1. Zones are grouped by source URL in plugin outline
2. Click URL to open in browser (with ?undrifter=load)
3. Extension automatically loads zones for that URL
4. Edit or delete zones directly in extension
5. Changes sync to server in real-time
6. Delete all zones from a URL via plugin UI
---
What's Implemented
| Feature | Status |
| --------- | -------- |
| Zone selection with crop handles | ✅ |
| DOM extraction (semantic nodes) | ✅ |
| Semantic inference (ARIA, classes) | ✅ |
| Screenshot capture + injection | ✅ |
| Batch zone workflow | ✅ |
| Local server (Node.js + Hono) | ✅ |
| FigJam + Figma plugin | ✅ |
| Outline view with tree structure | ✅ |
| Paste & Link to text layers | ✅ |
| Component property support | ✅ |
| Inspector panel buttons (RelaunchData) | ✅ |
| Figma Variables creation | ✅ |
| Shared plugin data (cross-plugin) | ✅ |
| Link sync (batch update) | ✅ |
| Image proxy for external images | ✅ |
| Figma OAuth integration | ✅ |
| Manual content from plugin | ✅ |
| URL-based zone grouping | ✅ |
| Delete zones by URL | ✅ |
| Zone loading from plugin links | ✅ |
| Edit/delete zones in extension | ✅ |
| Smart fallback for empty zones | ✅ |
| Whitespace normalization | ✅ |
| Accessibility text filtering | ✅ |
| Single zone injection | ✅ |
| Copy content to clipboard | ✅ |
| Image placement on canvas | ✅ |
| Full Page capture (scroll-to-load, no interaction) | ✅ |
| Variable stickies (cloneWidget, Refresh creates) | ✅ |
| Status workflow (draft/review/approved) | ✅ |
| Multi-instance (CopyDoc per board/file) | ✅ |
| Oy branding | ✅ |
| Settings view (feature flags) | ✅ |
| Injection width from selection | ✅ |
| FigJam widget (CopyDoc editor) | ✅ |
| Create CopyDoc from FigJam | ✅ |
| Variable sync (FigJam → Figma) | ✅ |
| File bindings (FigJam ↔ Figma) | ✅ |
AI enrichment (Bumbler) — where you see the AI
Optional local LLM: use Ollama (OLLAMA_HOST, OLLAMA_MODEL, OLLAMA_ENABLED=true) or OpenClaw (OPENCLAW_ENABLED=true, OPENCLAW_URL) in server .env. See docs/AI_FEATURES.md for full API and configuration.
- Extension — Zone name suggestion and semantics enrichment when adding a zone (optional toggles). Select mode: Out / In to move selection one level in the DOM. Clean-structure at intake uses semantic vocabulary and base corpus. Settings toggles stacked and aligned; Show trace off by default.
- Plugin — AI status (shows "AI: ✓ ready" or "AI: unavailable"); Suggest zone name in "Start from blank"; Clean with AI in Structure tab (atomic design, semantic names, dedupe); Feed Gwen (good/bad) to add examples; variable names from semantics; search by semantics.
- Landing page (https://localhost:3001) — Home, Purpose, Zones, Docs, API, Logs (server log buffer for debugging). Chat with Gwen knows the semantic vocabulary and atomic levels. Create zone from last message creates a zone from a description (no scrape).
---
What's NOT Implemented (Future)
- Cloud sync option
- Dev Mode codegen integration
- Variable binding UI in plugin
---
Non-Goals
- Do NOT convert arbitrary HTML into Figma layout
- Do NOT attempt perfect design fidelity
- Do NOT scrape behind paywalls or authentication
---
Tech Stack
| Component | Stack |
| ----------- | ------- |
| Extension | MV3 + React + Vite + CRXJS + Zustand |
| Server | Node.js/Bun + Hono + dotenv |
| Plugin | Figma Plugin API + Vite |
| Widget | Figma Widget API + esbuild |
| Shared | TypeScript types |
---
Version History
| Version | Name | Notes |
| --------- | ------ | ------- |
| 0.1.0 | Throcken | Initial implementation |
| 0.1.1 | Throcken | Native Figma integration: Variables, RelaunchData, Component Properties, Shared Plugin Data, crop handles, semantic inference |
| 0.1.2 | Throcken | URL-based management, zone loading from links, edit/delete in extension, smart fallback, whitespace normalization, a11y filtering, Oy branding |
| 0.2.0 | Bumbler | Ollama integration: zone-name enrichment (local LLM); Bumbler version naming |
| 0.2.x | Bumbler | Settings tab, layout-width injection, span/div extraction, screenshot to server, nav icon stacking; OpenClaw option; vocabulary + base corpus; create zone from chat; landing Logs tab; extension Out/In level selection; Full Page capture (scroll-to-load, avoids sticky/absolute overlap); expand Show More (skips external links); collapsed body copy extraction; enrich troubleshooting |
| 0.2.x | Bumbler | FigJam widget — create CopyDoc in FigJam, sticky-note UI, edit variables, link to Figma design, sync vars; POST /figjam/items, GET/PATCH /variables, file bindings |
| 0.2.x | Bumbler | Multi-instance — per-board CopyDoc; variable stickies (cloneWidget on Add, Refresh creates); status workflow; full-page capture no-interaction; expand skips gallery/carousel |
---
"Long days and pleasant nights, sai."