Phase C — Organisations as pages on Obsidian 1.13's settings API #5

Open
opened 2026-09-14 16:44:28 +01:00 by cruelacid · 2 comments
Owner

Part 3 of 3 of the sessions/device-roster plan (14 Sep 2026). Follows #2 and #4; supersedes the spike half of #1.

Spike first (time-boxed, its own checkpoint, scratch branch)

A tab returning getSettingDefinitions() that answers:

  1. Can a render row host a whole existing section (the sign-in card, a rowGroup) with our CSS intact?
  2. Does update() keep scroll position, focus and an open <details>?
  3. Does page: () => SettingPage open, get hide() on back/close, and can its display() reuse loadAccount with the generation guards?
  4. Do the existing e2e DOM polls (.setting-item, h4, rowGroup markup) still find what they look for?
    Fallback if 1 or 3 fails: in-tab route with breadcrumbs on the imperative tab (Relay's ManageRelay pattern); the content below is unchanged.

Shape

  • Signed out → one render row carrying the sign-in card.
  • Signed in → Account (signed in as / sign out, display name, passphrase, Signed-in vaults, passkeys), Organisations (one page per membership: desc role · region, displayValue "n of m devices", status:'warning' when not added on this device or suspended/over quota; Create / Join rows), Invitations, Shared folders in this vault, Attachments, Advanced.
  • OrganisationPage extends SettingPage: Plan & storage, Members (roster, Invite…, Share link, sent invitations), Devices (the roster with Remove / Add this device / rename), Shared folders of this organisation, Leave / Close. Subscribes to the fan-out in display(), releases in hide(), reuses the per-section loaders and slices.
  • The fan-out calls update() on memberships and refreshDomState() otherwise.

Checkpoint

  • spike verdict recorded here and on #1
  • unit: definitions-to-sections map; page subscribes/unsubscribes (leak test)
  • e2e re-pinned: open a page, device roster and members roster inside it, back navigation, no-stray-text audit and brand inset on the home screen, liveness with a page open, both themes screenshotted
  • mutation checks: a page that does not unsubscribe in hide(); update() not called on memberships
  • plugin installed into both vaults; no server change
Part 3 of 3 of the sessions/device-roster plan (14 Sep 2026). Follows #2 and #4; supersedes the spike half of #1. ## Spike first (time-boxed, its own checkpoint, scratch branch) A tab returning `getSettingDefinitions()` that answers: 1. Can a `render` row host a whole existing section (the sign-in card, a `rowGroup`) with our CSS intact? 2. Does `update()` keep scroll position, focus and an open `<details>`? 3. Does `page: () => SettingPage` open, get `hide()` on back/close, and can its `display()` reuse `loadAccount` with the generation guards? 4. Do the existing e2e DOM polls (`.setting-item`, `h4`, `rowGroup` markup) still find what they look for? Fallback if 1 or 3 fails: in-tab route with breadcrumbs on the imperative tab (Relay's ManageRelay pattern); the content below is unchanged. ## Shape - Signed out → one render row carrying the sign-in card. - Signed in → **Account** (signed in as / sign out, display name, passphrase, Signed-in vaults, passkeys), **Organisations** (one `page` per membership: `desc` role · region, `displayValue` "n of m devices", `status:'warning'` when not added on this device or suspended/over quota; Create / Join rows), **Invitations**, **Shared folders in this vault**, **Attachments**, **Advanced**. - `OrganisationPage extends SettingPage`: Plan & storage, Members (roster, Invite…, Share link, sent invitations), Devices (the roster with Remove / Add this device / rename), Shared folders of this organisation, Leave / Close. Subscribes to the fan-out in `display()`, releases in `hide()`, reuses the per-section loaders and slices. - The fan-out calls `update()` on `memberships` and `refreshDomState()` otherwise. ## Checkpoint - [ ] spike verdict recorded here and on #1 - [ ] unit: definitions-to-sections map; page subscribes/unsubscribes (leak test) - [ ] e2e re-pinned: open a page, device roster and members roster inside it, back navigation, no-stray-text audit and brand inset on the home screen, liveness with a page open, both themes screenshotted - [ ] mutation checks: a page that does not unsubscribe in `hide()`; `update()` not called on `memberships` - [ ] plugin installed into both vaults; no server change
Author
Owner

Spike verdict (branch spike/settings-pages, commit on it; measured in a real Obsidian 1.13.7 via test/multi/spike-settings.test.ts):

  1. Render rows host our sections. rowGroup inside a render row keeps the native .setting-group > .setting-items markup (2 rows). The sign-in card renders in a render row with the right fill (rgb(217, 182, 90)), 3 provider rows and its 420 px column — but the host .setting-item is display:flex with 20 px padding, so the card's auto margins resolve to 24 px / 16 px, not centred. Fix: neutralise the host row we own (display:block; padding:0) before drawing into it.
  2. update() is a full rebuild. Same scroll container, scroll kept (3061 → 3012 with content reflow), focus on a control kept, <details> returns closed (new node), render-row cleanups run each time, getSettingDefinitions() is called each time. So: update() only when the definitions change (a membership added/removed); every other refresh stays in place via the existing per-section loaders and generation guards.
  3. A page: () => SettingPage works end to end. Native titlebar (.setting-page-titlebar with .setting-page-back-button), display() on open, hide() on back and on close, displayValue shown on the entry, and the page's display() reused displayAccount (Members / Attachments / Devices rendered inside it).
  4. E2E re-pinning needed: .setting-item rows and rowGroup markup still match; section h4s move into pages; render-row hosts carry empty .setting-item-name elements the stray-text audit must skip; the brand header becomes a render row.

Decision: build on the declarative API with imperative render rows and one OrganisationPage per membership. No fallback needed.

Spike verdict (branch `spike/settings-pages`, commit on it; measured in a real Obsidian 1.13.7 via `test/multi/spike-settings.test.ts`): 1. **Render rows host our sections.** `rowGroup` inside a render row keeps the native `.setting-group > .setting-items` markup (2 rows). The sign-in card renders in a render row with the right fill (`rgb(217, 182, 90)`), 3 provider rows and its 420 px column — but the host `.setting-item` is `display:flex` with 20 px padding, so the card's auto margins resolve to 24 px / 16 px, not centred. Fix: neutralise the host row we own (`display:block; padding:0`) before drawing into it. 2. **`update()` is a full rebuild.** Same scroll container, scroll kept (3061 → 3012 with content reflow), focus on a control kept, `<details>` returns closed (new node), render-row cleanups run each time, `getSettingDefinitions()` is called each time. So: `update()` only when the definitions change (a membership added/removed); every other refresh stays in place via the existing per-section loaders and generation guards. 3. **A `page: () => SettingPage` works end to end.** Native titlebar (`.setting-page-titlebar` with `.setting-page-back-button`), `display()` on open, `hide()` on back and on close, `displayValue` shown on the entry, and the page's `display()` reused `displayAccount` (Members / Attachments / Devices rendered inside it). 4. **E2E re-pinning needed**: `.setting-item` rows and `rowGroup` markup still match; section `h4`s move into pages; render-row hosts carry empty `.setting-item-name` elements the stray-text audit must skip; the brand header becomes a render row. Decision: build on the declarative API with imperative render rows and one `OrganisationPage` per membership. No fallback needed.
Author
Owner

Checkpoint C, 14 Sep 2026 — commit 9806346 on main.

  • Pane rendered from getSettingDefinitions(); one native page per organisation (plan/storage, members, devices, invite + share link). Home entries say the roster standing and carry a warning mark.
  • Unit 309 green, lint and typecheck clean, mirror --check clean. Full multi-vault e2e: 52 passed, 1 failed = the pre-existing recovery flake (#3), which passed on its own rerun. identity.test.ts 10/10 on a clean run.
  • Mutation checks (all caught): pagesKey ignoring the device standing; pagesKey moving on a token; pageName not numbering duplicates; pagesChanged always rebuilding; pagesChanged ignoring mode/sign-in; the page not binding in display() (4 e2e failures); join/create paths not announcing memberships (the stray-text audit fails — this was a real bug found by the run: an organisation joined with the pane open never appeared).
  • Screenshots in both themes reviewed (home + organisation page).
  • Plugin installed into Mind Palace and Test Vault 2 (sha256 6dd26438…, backups beside it). No server change.

Still to confirm on the user's machine: the two vaults show the new pane after a reload, and an organisation's page opens and comes back.

Checkpoint C, 14 Sep 2026 — commit 9806346 on main. - Pane rendered from getSettingDefinitions(); one native page per organisation (plan/storage, members, devices, invite + share link). Home entries say the roster standing and carry a warning mark. - Unit 309 green, lint and typecheck clean, mirror --check clean. Full multi-vault e2e: 52 passed, 1 failed = the pre-existing recovery flake (#3), which passed on its own rerun. identity.test.ts 10/10 on a clean run. - Mutation checks (all caught): pagesKey ignoring the device standing; pagesKey moving on a token; pageName not numbering duplicates; pagesChanged always rebuilding; pagesChanged ignoring mode/sign-in; the page not binding in display() (4 e2e failures); join/create paths not announcing memberships (the stray-text audit fails — this was a real bug found by the run: an organisation joined with the pane open never appeared). - Screenshots in both themes reviewed (home + organisation page). - Plugin installed into Mind Palace and Test Vault 2 (sha256 6dd26438…, backups beside it). No server change. Still to confirm on the user's machine: the two vaults show the new pane after a reload, and an organisation's page opens and comes back.
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#5
No description provided.