DEV Community

Qonspekt
Qonspekt

Posted on • Originally published at qonspekt.github.io

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

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?

Top comments (0)