← Back to Blog
AI/ML2026-05-13Β·18 min read

I built a BYOK browser tool that turns any article into atomic Obsidian notes

By Qonspekt

I kept procrastinating on taking notes from articles I read. The cycle was always the same: read something interesting, tell myself I'll write proper notes later, never do it. So I built a tool to do it automatically.

What Qonspekt does

Paste any article β†’ Claude AI extracts the key concepts β†’ you get 3–7 atomic Markdown notes, ready to drag into Obsidian.

Try it: https://qonspekt.github.io/qonspekt/

Each note gets:

  • YAML frontmatter (title, tags, aliases)
  • [[wikilinks]] connecting related concepts from the same article
  • ## Sources section with the original URL
  • Proper filename (concept-slug.md)

The technical approach: single HTML file, no backend

The whole thing is one HTML file. No framework, no build step, no server.

const response = await fetch('https://api.anthropic.com/v1/messages', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'x-api-key': userApiKey,
    'anthropic-version': '2023-06-01',
    'anthropic-dangerous-direct-browser-access': 'true',  // official BYOK header
  },
  body: JSON.stringify({ model, max_tokens: 4096, system, messages })
});

Enter fullscreen mode Exit fullscreen mode

The anthropic-dangerous-direct-browser-access: true header is Anthropic's official way to support BYOK browser tools. Your key goes from your browser directly to Anthropic β€” I have no server to log anything.

Getting consistent JSON from Claude

The trickiest part: Claude sometimes wraps output in markdown code fences even when told not to.

const raw = data.content?.[0]?.text || '';
const match = raw.match(/\[[\s\S]*\]/);  // extract JSON array regardless of wrapping
if (!match) throw new Error('Could not parse response');
const notes = JSON.parse(match[0]);

Enter fullscreen mode Exit fullscreen mode

This regex approach handles both clean JSON and fenced output reliably.

Serverless sharing

After generating, there's a "Share" button that base64-encodes all notes into the URL hash:

const enc = btoa(unescape(encodeURIComponent(JSON.stringify(notes))));
const url = location.origin + location.pathname + '#shared=' + enc;
navigator.clipboard.writeText(url);

Enter fullscreen mode Exit fullscreen mode

On load, if the hash starts with #shared=, it decodes and renders the notes in read-only mode. No server, no database, no account needed for the recipient.

The system prompt

Getting the right structure took iteration. Key constraints that worked:

- Extract 3-7 key concepts, write one atomic note per concept
- Each note is self-contained but uses [[wikilinks]] to reference 
  related concepts from the same batch
- 150-280 words per note body
- Tags: 2-4, lowercase, hyphens for spaces
- Return ONLY a valid JSON array, no markdown fences

Enter fullscreen mode Exit fullscreen mode

The "same batch" constraint for wikilinks is important β€” it makes the notes interconnected without hallucinating links to notes that don't exist.

Cost

~$0.003 per article with Claude Haiku 4.5. New Anthropic accounts get free credits.

Source

MIT licensed: https://github.com/Qonspekt/qonspekt

Would love feedback on the note structure or the prompt. What frontmatter fields do you typically use in your vault?