Perplexed's 44 settings are searchable now, and the tab can't hide its own rows
Perplexed 0.4.0 rebuilds its settings on Obsidian 1.13: every setting is searchable, lists are lists, and a bad row can't blank the tab.
Why Care?
Perplexed is the research plugin in the Lossless family: Perplexity, Claude, Gemini, Perplexica / Vane, LM Studio, and Exa, plus prompt libraries and per-folder research templates. Configuring all that takes 44 settings rows. On Obsidian 1.13 and later, not one of them appeared in Obsidian's settings search, because the tab was drawn by hand in an API that predates search.
Hand-drawn tabs have a worse failure mode too. On Image Gin, a tab of the same style broke on Obsidian 1.14: one row threw while drawing, and every section below it vanished. Perplexed's tab had 14 sections stacked the same way.
0.4.0 rebuilds the tab on Obsidian 1.13's declarative settings API. Obsidian now draws the rows itself and indexes all of them for search. Type "Exa", "system prompt", or "LM Studio endpoint" into settings search and you land on the row.
What's New?
Every setting is searchable. The three rows that share a name across providers ("Endpoint", "Default model", "Request body template") carry provider aliases, so "Perplexica endpoint" finds the right one.
Lists are lists. System preambles, user preambles, and the frontmatter whitelist were comma-separated text, with a
:return-imagessuffix syntax for user preambles. They are now editable lists: add, delete, drag to reorder, and a dropdown for when a user preamble attaches.Number fields say no. The Exa context budget, request timeout, and max images reject anything but a whole number above 0, with a message. 0.3.1 silently dropped those edits.
Exa's settings follow its switch. The key, endpoint, and budget show only while "Use Exa retrieval" is on.
Nothing to migrate. Every key in an existing
data.jsonis read and written in place. Command IDs are unchanged, so hotkeys keep working.Requires Obsidian 1.13. Older Obsidian versions stay on 0.3.1.
How it works
The tab implements getSettingDefinitions() and returns plain objects: groups with headings, rows with a control (text, textarea, toggle, dropdown, number, folder), three list definitions, and one render row for the Re-seed templates button. There is no display() override, so Obsidian 1.13+ never runs hand-drawn code for this tab.
Perplexed stores its prompts in a nested prompts object, so the tab overrides getControlValue / setControlValue with dot paths (prompts.enhancePrompt). The setter refuses any key that DEFAULT_SETTINGS lacks, so a typo can't grow data.json. It trims the same fields 0.3.1 trimmed and pushes prompt edits to the live prompts service. The getter pretty-prints the three JSON request templates when they parse, as the old tab did.
The bug the lists found
Turning the comma-separated fields into lists meant editing arrays in place, and that exposed a quiet bug. loadSettings() did Object.assign({}, DEFAULT_SETTINGS, savedData): a shallow copy. In a vault whose data.json had no prompts (a fresh install, for one), settings.prompts was DEFAULT_SETTINGS.prompts. Editing a prompt edited the defaults, and Reset prompts to default then restored the user's own edit until Obsidian restarted. The fix merges a structuredClone of the defaults, prompts key by key, and a test asserts that list and prompt edits leave DEFAULT_SETTINGS untouched.
Tests, for the first time
Perplexed had no tests. It now has 24, run by pnpm test with no test framework. The harness, copied from Cite Wide, bundles tests/*.test.ts with esbuild against a runtime stub of the obsidian module and runs them with node:test. The stub's addSettingTab() calls getSettingDefinitions() immediately, as Obsidian 1.13+ does, so a tab that throws fails onload() in the test the way it would in the app.
The tests went in red first, against the old tab: 20 failing specs describing the new one, and 4 guards that had to stay green throughout. The guards:
All 26 command IDs register, with no duplicates.
A
data.jsonwith the live vault's 33-key shape loads and keeps every key. The fixture copies only key names and types; every value is a dummy.
The production main.js was also loaded outside Obsidian against the same stub and fixture: 26 commands, 69 setting definitions.
Also in this release
Toolchain:
obsidian1.13.1 (pinned; it had been floating onlatest), ESLint 10.12, typescript-eslint 8.71.1, eslint-plugin-obsidianmd 0.4.2, esbuild 0.28.2. TypeScript holds at 6.0.3.pnpm-workspace.yamlgainsallowBuildsfor pnpm 11+.onload()no longer writes the full settings object, API keys included, to the developer console on every load.Notices and the Anthropic key description no longer claim keys are read from
.env. Nothing reads.env.README: the install steps described an "Install plugin from file" button Obsidian doesn't have, the command palette shortcut was wrong, and the developer section still carried the starter template's
builtin-modulesbuild script. All rewritten.
What's next
Plan step 5 (a real-vault check) and step 6 (fast-forward development → main → master, tag 0.4.0, verify the release assets and attestations). Until the tag, the release notes stay in Draft.