Adopt Obsidian 1.13's declarative settings API for the plugin settings tab #1

Closed
opened 2026-09-14 09:24:30 +01:00 by cruelacid · 2 comments
Owner

Why

The settings tab is imperative: display() empties the container and rebuilds everything, from fetches that are a snapshot of the instant the pane opened. That is why a freshly signed-in vault listed itself as "not connected" until the pane was closed and reopened (fixed by an event fan-out plus a slow poll, with sections that own and rebuild their own containers).

Obsidian 1.13.0 deprecates display() and adds a declarative API that is the platform's own answer to a pane that stays current.

What the API offers (from obsidian.d.ts 1.13.1)

  • getSettingDefinitions() returns definitions; update() is "called by addSettingTab() and by dynamic tabs when their data changes"; refreshDomState() re-evaluates visible/disabled predicates in place with no re-render.
  • render rows: an imperative escape hatch render(setting, group) that may return a cleanup function, invoked before the row is torn down.
  • Native group and list definitions with heading, empty state, drag-to-reorder and an add affordance — the markup we currently emit by hand via rowGroup().
  • Search integration for free (name, desc, aliases).

What makes it a rewrite rather than a fix

  • All or nothing: once definitions are returned, display() is never called.
  • Standard controls write through on change via getControlValue/setControlValue, so the "type, then Save" rows (display name, share link) change shape.
  • The branded sign-in card, provider marks and the whole signed-out screen become render rows; whether a render row can escape the row layout to draw a centred card is not answered by the typings.
  • The end-to-end assertions are structural (.setting-item, h4 headings, row-group markup) and would need re-pinning against declarative output.
  • The API is weeks old (@since 1.13.0); expect churn.

Spike first — three unknowns

  1. Does update() preserve scroll position, focus and an open <details>?
  2. Can a render row carry the sign-in card (centred column, brand header)?
  3. How do async data + update() behave under the existing end-to-end DOM polls?

Groundwork already in place

The current pane fix was built to feed this migration, not fight it:

  • the plugin's onChange(reason) fan-out is what a declarative tab would subscribe to in order to call update();
  • the per-section loaders with generation guards are its data fetchers unchanged;
  • the account response is already split into slices with change detection, which is the shape definitions would consume.

Not blocking

Our manifest already requires 1.13.7, so no version gate is needed.

## Why The settings tab is imperative: `display()` empties the container and rebuilds everything, from fetches that are a snapshot of the instant the pane opened. That is why a freshly signed-in vault listed itself as "not connected" until the pane was closed and reopened (fixed by an event fan-out plus a slow poll, with sections that own and rebuild their own containers). Obsidian 1.13.0 deprecates `display()` and adds a declarative API that is the platform's own answer to a pane that stays current. ## What the API offers (from `obsidian.d.ts` 1.13.1) - `getSettingDefinitions()` returns definitions; `update()` is "called by addSettingTab() and by dynamic tabs when their data changes"; `refreshDomState()` re-evaluates `visible`/`disabled` predicates in place with no re-render. - `render` rows: an imperative escape hatch `render(setting, group)` that may return a cleanup function, invoked before the row is torn down. - Native `group` and `list` definitions with heading, empty state, drag-to-reorder and an add affordance — the markup we currently emit by hand via `rowGroup()`. - Search integration for free (name, desc, aliases). ## What makes it a rewrite rather than a fix - All or nothing: once definitions are returned, `display()` is never called. - Standard controls write through on change via `getControlValue`/`setControlValue`, so the "type, then Save" rows (display name, share link) change shape. - The branded sign-in card, provider marks and the whole signed-out screen become `render` rows; whether a render row can escape the row layout to draw a centred card is not answered by the typings. - The end-to-end assertions are structural (`.setting-item`, `h4` headings, row-group markup) and would need re-pinning against declarative output. - The API is weeks old (`@since 1.13.0`); expect churn. ## Spike first — three unknowns 1. Does `update()` preserve scroll position, focus and an open `<details>`? 2. Can a `render` row carry the sign-in card (centred column, brand header)? 3. How do async data + `update()` behave under the existing end-to-end DOM polls? ## Groundwork already in place The current pane fix was built to feed this migration, not fight it: - the plugin's `onChange(reason)` fan-out is what a declarative tab would subscribe to in order to call `update()`; - the per-section loaders with generation guards are its data fetchers unchanged; - the account response is already split into slices with change detection, which is the shape definitions would consume. ## Not blocking Our manifest already requires 1.13.7, so no version gate is needed.
Author
Owner

Spike done on 14 September 2026 under #5: the three unknowns are answered there (update() rebuilds and keeps scroll/control focus but not an open details; a render row can carry the sign-in card once its host row is neutralised; the page lifecycle is clean). Migration proceeds as Phase C of the sessions/devices plan; this issue is superseded by #5.

Spike done on 14 September 2026 under #5: the three unknowns are answered there (update() rebuilds and keeps scroll/control focus but not an open details; a render row can carry the sign-in card once its host row is neutralised; the page lifecycle is clean). Migration proceeds as Phase C of the sessions/devices plan; this issue is superseded by #5.
Author
Owner

Superseded: the declarative-API question was answered by the spike on #5 and the pane now renders from definitions (9806346).

Superseded: the declarative-API question was answered by the spike on #5 and the pane now renders from definitions (9806346).
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Nectenda/nectenda#1
No description provided.