Perplexed 0.2.0 — Google Gemini joins the lineup, with the per-claim citations Claude's web_search can't keep
Ask Gemini lands as the fourth research provider — Google Search grounded, with per-segment citation attribution that survives the full round-trip from model to vault. Templates gain a vault-visible partials/preambles layer so shared rules (mermaid discipline, citation enforcement, image placement) stop being duplicated across files. Eight settings sections that had been silently hidden for weeks finally render again. Three system-prompt textareas grow from 200px cramped slots to full-width rows. And the seeder stops yelling on every plugin load.
Why care?
If you're filling research notes in Obsidian — concept profiles, source dossiers, vocabulary entries, anything where the source citation matters as much as the body — Perplexed 0.2.0 changes two things that show up the moment you use it:
You now have a fourth research provider, and it cites better than the three before it. Ask Gemini joins Ask Perplexity, Ask Claude, and Ask Perplexica with full Google Search grounding turned on by default. Where Claude's newer web_search tool drops per-claim citations on the round-trip through its dynamic-filtering sandbox (sources come back, attribution doesn't), Gemini's groundingSupports[] carries the segment-to-source mapping intact — so the ### Citations footer that lands in your note is actually attached to the prose above it, with the verbatim quote per source.
Your shared editorial rules stop being duplicated across templates. Four directory templates (concept, vocabulary, source, toolkit) used to repeat the same mermaid-discipline / citation-enforcement / image-placement / research-framing guidance inside each one, or hardcode it in TypeScript where you couldn't touch it. Now those rules live in vault-visible partials/ and preambles/ folders. Fix the mermaid rules once in partials/mermaid-discipline.md and every template picks the fix up on the next generation.
A third thing matters in the moment but won't appear in any user-facing copy: the settings tab finally renders all of its sections. A one-line DOM-API bug — addClass being passed a space-separated string where it expects a single token — was throwing on settings open and aborting display() mid-render. Every settings row after that point silently disappeared. For weeks. The fix was eight call sites; the symptom was "Perplexed lost half its settings." Both halves are visible now.
What's new?
Ask Gemini — Google Search grounding with per-segment citation attribution
Gemini ships as the fourth provider via a dedicated Ask Gemini modal, mirroring the Ask Claude shape (question textarea, model dropdown, behavior toggles) with Gemini-flavored visual identity. Auth is a simple API key from aistudio.google.com — no GCP project, no OAuth, no service account. Paste the key into Settings → Perplexed → Gemini (Google), pick your model, ask.
| Provider | Endpoint | Cost | Best for |
| Perplexity | api.perplexity.ai | Paid | Source-cited research with consistent citation formatting |
| Anthropic Claude | api.anthropic.com | Paid | Longer-form reasoning with web_search and adaptive thinking |
| Google Gemini (new) | generativelanguage.googleapis.com | Free tier + paid tiers | Google Search grounding with per-segment citation attribution |
| Perplexica / Vane | localhost:3030 (self-hosted) | Free | Privacy-sensitive research; runs entirely on your machine |
| LM Studio | localhost:1234 (local) | Free | Local-only inference, no network |
The default model is gemini-flash-latest — Google's "always-current Flash" alias, which is free-tier-friendly and tracks Google's own quickstart default. gemini-pro-latest, plus the pinned gemini-2.5-pro and gemini-2.5-flash versions, are all selectable in the modal and in plugin settings.
Why Gemini's citations are different
We dissected an actual gemini-flash-latest response with curl before wiring the parser. Two layers carry provenance:
| Layer | Field | What it gives you |
| Page-level | groundingChunks[] | URL + title per page Gemini consulted |
| Segment-level | groundingSupports[] | Text span (segment.text) → indices into groundingChunks[] |
The plugin walks both. Page-level chunks become URL-and-title fallbacks; segment-level supports enrich them with the verbatim quote Gemini grounded against. Where Claude's web_search_20260209 post-processes search results in a code-execution sandbox and the per-claim attachment doesn't survive the round-trip (text blocks come back with citations: null), Gemini's groundingSupports[] ships in the final response unmodified.
Two Gemini-specific quirks the curl exposed (and how we handle them)
chunk.web.uriis a short-livedvertexaisearch.cloud.google.comredirect. It expires roughly 30 days after the response — so naive "just write the URL we got" means every cited note rots on a clock. The plugin resolves each redirect via Obsidian'srequestUrl(Node-side, no CORS) to the real destination page before writing, and while it's there parses<link rel="canonical">/<meta property="og:url">for the durable source URL and<meta property="og:title">/<title>for the real page title. So a citation that would naively render as[bloomberg.com](vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIY...)instead lands as[Bloomberg Beta — Bloomberg LP](https://www.bloomberg.com/company/bloomberg-beta/). Falls back to the redirect URL on any failure (timeout at 5s, network error, parse miss) so the citation stays navigable today even if it rots in 30.searchEntryPoint.renderedContentis 5KB of inline-styled HTML. Google's grounding terms ask that this Search Suggestions chip be displayed when google_search is used; the chip itself is<style>-tag-prefixed HTML that Obsidian's Markdown renderer strips, leaving orphan<div>shells. The plugin substitutes a Markdown-native### Google Searchessection: a bullet list of thewebSearchQueries[]Gemini actually issued, each linked togoogle.com/search?q=…so the user can re-run the suggested search. Same end-user behavior, rendered in a form Obsidian can actually display.
The same ### Citations footer shape Perplexity and Claude already use means Cite-Wide's hex-substitution pass works on Gemini output too — the provider is plug-compatible with the existing citation pipeline.
Partials + preambles — shared guidance, vault-visible, no more duplicated rules
The four directory templates (concept, vocabulary, source, toolkit) historically duplicated their mermaid-discipline / citation-enforcement / image-placement / research-framing guidance inside each template, with some of it hardcoded in TypeScript constants users couldn't edit. The 0.2.0 release moves that guidance into two peer folders alongside templates/:
zz-cf-lib/
├── templates/ (your four profile templates)
├── partials/ (NEW — referenced from templates via {{include: name}})
│ └── mermaid-discipline.md
└── preambles/ (NEW — auto-attached to every Perplexity request)
├── inline-citation.md (was a hardcoded TS constant)
├── image-placement.md (was a hardcoded TS constant)
└── research-framing.md (was a TS function) {{include: name}}— recursive, depth-limited, cycle-detected splice. Reference a partial from any template; missing files surface as inline[[include: name — file not found]]markers so typos stay visible.Preambles auto-attach to every Perplexity request, with bundled defaults as fallback. Settings tab gained four new rows: Partials root, Preambles root, System preambles, User preambles.
Per-template overrides in the
cftfence:preambles: { system: [...], skip-user: [...], skip-all: true }.
Fix the mermaid quoting rule once in partials/mermaid-discipline.md — every future template generation picks it up.
Settings tab — eight hidden sections, back in view
A long-standing addClass('perplexed-json-textarea is-tall') bug at two specific DOM API call sites (Article Generator template + Deep Research template textareas) was throwing InvalidCharacterError: Failed to execute 'add' on 'DOMTokenList': The token provided contains HTML space characters because addClass accepts a single token, not a space-separated string. The throw aborted display() mid-render, silently breaking every settings section beneath it.
Same shape of bug as the May 19 fix that converted eight activeDocument.createEl calls to containerEl.createEl (which resurrected eleven hidden Perplexed / Claude / Perplexica / LM Studio settings sections). Today's fix swaps both addClass('a b') to addClasses(['a', 'b']). Sections that hadn't rendered in weeks now render again.
System-prompt textareas — full-width rows instead of 200px slots
The three system-prompt settings (Perplexity / Perplexica-Vane / LM Studio) used Obsidian's Setting.addTextArea, which crammed multi-line prompt content into the ~200px right-edge of the Setting row at 2 lines tall. For a multi-paragraph system prompt, that's a porthole into a much larger document.
Each now gets two sibling elements: a Setting row with name + description on top, then a full-width 3-line textarea directly below (resizable vertically). New CSS class .perplexed-prose-textarea — body font, 80px min-height, focus ring matching the modal style, full container width.
Placeholder-text settings stayed as compact single-line rows because that's the right shape for them.
Template seeder — idempotent at both folder and file layers
Two race-window bugs in the seeder were producing red "Folder already exists" / "File already exists" console lines on every plugin load. Same root cause for both: Obsidian's in-memory file index lags the adapter write by a tick, so getAbstractFileByPath(path) === null followed immediately by createFolder / create races and throws — even though the index check said it was safe.
Both errors now get swallowed by helpers (ensureFolder / safeCreateFile) that test the error message and rethrow anything that isn't an "already exists" race. Seeder is fully idempotent now.
Network-error UX in the Perplexity stream
ERR_NETWORK_CHANGED (WiFi roam, VPN reconnect, sleep/wake) and its cousins (socket-drop, idle-timeout, abort) now get classified and translated into user-language notices:
| Error class | Old behavior | New behavior |
NETWORK_CHANGED | 30-line stack trace inline in the note | Network changed mid-stream … Re-run the query — no partial response was saved. |
| Idle timeout | Bare stream went idle for 90s | Perplexity stream stalled (likely API back-pressure or rate limit). Re-run the query. |
| Connection drop | failed to fetch | Connection dropped before Perplexity finished. Re-run the query. |
| User abort | AbortError | Request aborted. |
Mid-stream resume isn't possible — Perplexity has no resume token — so honest re-run guidance beats a magic retry that silently produces a partial.
Ask Gemini modal — real CSS, Google-flavored, theme-aware
The Ask Gemini modal follows the proven wide-modal pattern from context-v/issues/Widen-Modals-in-Obsidian-using-CSS.md (the "attach the class to modalEl, not contentEl" doctrine that ended six months of "Obsidian doesn't let me size modals" frustration). On top of the structural pattern:
Gradient title text (blue → purple → red → yellow via
background-clip: text)Google four-color hairline under the header (
#4285f4/#ea4335/#fbbc04/#34a853at 55% opacity)Gemini-blue focus ring on the prompt textarea (consistent regardless of user theme)
Primary CTA flat blue at rest, blue→purple gradient on hover with elevated shadow
Theme tokens (
--background-*,--text-*,--font-text) for everything structural — light, dark, and community theme parity
Under the hood
Each substantive piece of work shipped in 0.2.0 has its own per-day changelog under changelog/:
| Date | Topic | Per-day changelog |
| 2026-05-19 | Partials + preambles — vault-visible shared rules; {{include: name}} directive; preambles folder auto-attaches to every request; per-template overrides via cft fence; one-line fix that resurrected 11 hidden settings sections (activeDocument.createEl → containerEl.createEl) | 2026-05-19_01.md |
| 2026-05-19 | Gemini provider lands — Ask Gemini modal, settings section, groundingMetadata parsing, per-segment citations via groundingSupports[], Markdown substitute for the searchEntryPoint HTML chip, response shape dissected with curl | 2026-05-19_02.md |
| 2026-05-20 | Obsidian-shipping pass — CORS-blocked fetch → requestUrl + canonical/og + <title> parsing; ?key= → X-goog-api-key header; default model → gemini-flash-latest; Gemini modal CSS; safeCreateFile race fix; ERR_NETWORK_CHANGED UX; system-prompt textareas promoted; addClass token bug fixed at two more call sites | 2026-05-20_01.md |
What's next
Hook Gemini into directory templates. A template declaring
provider: geminiin itscftfence should route through the Gemini service the same way existing templates route through Perplexity. The per-segment grounding shape is actually better-suited to citation-spec output than Perplexity's baresearch_results[].Extract the editorial-stance partial. The anti-incumbent stance is still duplicated across
concept-profile.mdandvocabulary-profile.md; extracting it intopartials/editorial-stance-anti-incumbent.mdis the obvious next move now that the partials machinery exists.Real page titles for non-Gemini providers too. The
requestUrl+ canonical-URL +<title>extraction we built for Gemini citation resolution generalizes — Perplexity citation post-processing currently keeps whatever title the source delivered, which is sometimes a domain. Same parse pipeline could enrich those too.Diff-aware regeneration for directory templates. Files with
cf_last_runstamped could be skipped on subsequent runs by default, with an explicit--forcemode for refreshes. Right now every folder-batch run regenerates every file.Per-domain canonical-URL parse heuristics. Some sites (Substack, Medium, news aggregators) put the canonical URL behind a paywall redirect or in a non-standard meta tag. A small per-host hint table could improve resolution accuracy on the long tail of grounding sources.
References
Release on GitHub: https://github.com/lossless-group/perplexed-plugin/releases/tag/0.2.0
Install from Obsidian: Settings → Community Plugins → Browse → "Perplexed"
Prior release:
0.1.1(0.1.2was a workflow/attestation pass — no user-facing changes)Gemini provider changelog:
changelog/2026-05-19_02.mdPartials + preambles changelog:
changelog/2026-05-19_01.mdObsidian-shipping pass changelog:
changelog/2026-05-20_01.mdWide-modal doctrine:
context-v/issues/Widen-Modals-in-Obsidian-using-CSS.mdUpstream providers: Perplexity, Anthropic Claude, Google Gemini, Perplexica / Vane, LM Studio