You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
-`scrollContainerRef?: React.Ref<HTMLDivElement>` — forwards a ref to the scroll
81
92
region (e.g. programmatic scroll-to-bottom).
82
93
83
-
## Title + description live in navigation metadata
94
+
## Title + description live in the shared settings registry
84
95
85
-
`apps/sim/app/workspace/[workspaceId]/settings/navigation.ts` is the single source
86
-
of truth. Every `NavigationItem` carries a one-line `description`; `SettingsPanel`
87
-
resolves both via `getSettingsSectionMeta(section)` and the
88
-
`SettingsSectionProvider` the settings shell wraps around the active section.
96
+
`apps/sim/components/settings/navigation.ts` is the single source of truth.
97
+
Each `SettingsSectionRegistryEntry` owns a `unified` projection with the default
98
+
description and gating metadata plus optional account, organization, and
99
+
workspace plane projections. Account and organization shells provide `plane`
100
+
and `section`, so the shared `SettingsPanel` resolves metadata with
101
+
`getSettingsSectionMeta(plane, section)`. The workspace adapter at
102
+
`app/workspace/[workspaceId]/settings/navigation.ts` resolves the mandatory
103
+
unified projection with its one-argument `getSettingsSectionMeta(section)` and
104
+
the workspace renderer passes that metadata to `SettingsSectionProvider`.
89
105
90
106
Adding a new settings page:
91
107
92
-
1. Add the `SettingsSection` id + a `NavigationItem` (with `label`**and**
93
-
`description`) in `navigation.ts`. Keep descriptions verb-first, one line,
94
-
~40–55 chars, in the product voice (see `.claude/rules/constitution.md`).
95
-
2. Render the component inside the shell's `effectiveSection` switch in
96
-
`settings/[section]/settings.tsx`.
97
-
3. Build the component body inside `<SettingsPanel>` — no shell, no title block.
108
+
Follow the procedure in `.claude/skills/add-settings-page/SKILL.md`. The
109
+
non-negotiable architecture invariants are:
110
+
111
+
- Every registry entry has a mandatory unified projection, workspace renderer,
112
+
and server-side direct-route outcome.
113
+
- Every optional account, organization, or workspace plane projection has its
114
+
corresponding renderer and access/feature gate.
115
+
- The component body uses `SettingsPanel`; associated unit tests and every
116
+
applicable literal browser contract change in the same PR.
98
117
99
118
## Text-scale tokens (no literal pixel sizes)
100
119
@@ -187,8 +206,9 @@ changes" modal:
187
206
(from `@/app/workspace/[workspaceId]/components/credential-detail`). The
188
207
in-view header **Discard** chip (via `SaveDiscardActions onDiscard`) is a
189
208
*reset to original* — distinct from the back-confirm's discard, which leaves.
190
-
-**`useSettingsBeforeUnload`** is mounted **once** in the settings shell
191
-
(`settings/[section]/settings.tsx`) — never add a per-page `beforeunload`.
209
+
-**`useSettingsBeforeUnload`** is mounted once per active shell boundary:
210
+
workspace `settings/layout.tsx` and `StandaloneSettingsShell` for account or
211
+
organization. Never add a per-page `beforeunload`.
192
212
-**Dirty *computation* stays local** (shapes differ: field-compare vs
193
213
normalize+stringify) — only how dirty is *consumed* is shared. Derive it (a
194
214
`const`/`useMemo`), never store it in `useState`.
@@ -218,9 +238,9 @@ exception — it lives outside `[section]` and keeps its own `CredentialDetailLa
218
238
A settings page is design-system-clean when:
219
239
220
240
-[ ] Its main return is a `<SettingsPanel>` (or `<>…<SettingsPanel>…</>` with modal siblings) — no hand-rolled shell/header/scroll/column.
221
-
-[ ] It renders **no** hand-rolled `<h1>`/description title block — the title comes from nav metadata.
241
+
-[ ] It renders **no** hand-rolled `<h1>`/description title block — the title comes from the shared settings registry.
222
242
-[ ] Header chips are in `actions`; a standalone search is in the `search` prop.
223
-
-[ ] Its `NavigationItem` has an accurate, consistent-length `description`.
243
+
-[ ] Its `SettingsSectionRegistryEntry` has an accurate, consistent-length `unified.description` and only the plane projections it supports.
224
244
-[ ] Detail sub-views and entitlement/loading gates keep their own chrome (intentional).
225
245
-[ ] If it has editable state: Save/Discard go through `SaveDiscardActions`, dirty is wired via `useSettingsUnsavedGuard` (called before any early-return gate), and there is **no** hand-rolled Save button / `beforeunload` / "Unsaved changes" modal.
226
246
-[ ] No business logic, handlers, or conditional rendering changed by the migration.
Copy file name to clipboardExpand all lines: apps/sim/AGENTS.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -221,6 +221,7 @@ export function useEntityList(workspaceId?: string) {
221
221
- Use `vi.hoisted()` + `vi.mock()` + static imports; do not use `vi.resetModules()` + `vi.doMock()` + dynamic imports except for true module-scope singletons.
222
222
- Do not use `vi.importActual()`.
223
223
- Prefer mocks and factories from `@sim/testing`.
224
+
- Before finishing a change that can affect observable settings routes, copy, visibility, authorization, entitlements, mutations, or persisted workflows, follow [`e2e/MAINTENANCE.md`](e2e/MAINTENANCE.md) and assess every applicable literal Playwright contract. Behavior-preserving refactors need focused verification, not artificial expectation edits.
0 commit comments