From d1d6ebaf5dc6b93cab8f3f5f292bb9c3a4ff388b Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Mon, 31 Aug 2026 17:01:41 -0400 Subject: [PATCH 1/9] ENG-2213 Settings catalog: address every setting by key and location MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Records what every setting row is called and where it lives, so a later change can find a setting without knowing which tab it is on. Nothing in the codebase held this. `zodSchema.ts` is storage shape — no labels, no descriptions, no locations — and `settingKeys.ts` holds block keys that diverge from what a row renders: `GLOBAL_KEYS.trigger` renders as "Graph-wide default", `PERSONAL_KEYS.personalNodeMenuTrigger` as "Personal override". Nor can it be collected at runtime: `Tabs` uses `renderActiveTabPanelOnly`, so inactive panels are out of the DOM and rows cannot self-register. So the label, group, description, doc link and location are authored, one entry per row. Everything else is derived rather than restated. Entries are keyed by `GROUP.member`, addresses are built by resolving that id against the `settingKeys.ts` constants, and the catalog is declared `satisfies Record`. A key added to `settingKeys.ts` is then a compile error until it is either given an entry or listed in `NonRowKeyId` with a reason, so a new setting cannot silently go missing. The remaining gap is the reverse direction: nothing checks that a row rendering `settingKeys` was classified as a row rather than exempted. An entry's `path` is the only field a relocation has to touch. Node-type settings give `path` as a function of the node uid, so one entry expands to one result per node type and adding a node type never edits this file. Six rows rendered without an anchor, so they had no stable address; each now passes the key constants already in scope at its call site. `SETTINGS_TAB_META` becomes the single source for tab labels, because a renamed tab would otherwise drift away from the locations recorded here. `SettingItemRow` falls back to the catalog for a description when the prop is omitted, so ENG-2187 can migrate call sites by deleting props rather than by copying text. Co-Authored-By: Claude Opus 5 --- .../src/components/settings/NodeConfig.tsx | 3 + .../components/settings/PageGroupPanel.tsx | 4 + .../settings/PreferencesGeneral.tsx | 2 +- .../roam/src/components/settings/Settings.tsx | 22 +- .../components/EphemeralBlocksPanel.tsx | 1 + .../settings/components/SettingItemRow.tsx | 19 +- .../settings/utils/settingsCatalog.ts | 569 ++++++++++++++++++ .../components/settings/utils/settingsTabs.ts | 55 ++ 8 files changed, 663 insertions(+), 12 deletions(-) create mode 100644 apps/roam/src/components/settings/utils/settingsCatalog.ts diff --git a/apps/roam/src/components/settings/NodeConfig.tsx b/apps/roam/src/components/settings/NodeConfig.tsx index abe796edf..0a68bbb80 100644 --- a/apps/roam/src/components/settings/NodeConfig.tsx +++ b/apps/roam/src/components/settings/NodeConfig.tsx @@ -17,6 +17,7 @@ import { CANVAS_KEYS, DISCOURSE_NODE_KEYS, SPECIFICATION_KEYS, + TEMPLATE_SETTING_KEYS, } from "~/components/settings/utils/settingKeys"; import DiscourseNodeSuggestiveRules from "./DiscourseNodeSuggestiveRules"; import { getNodeTagStyles } from "~/utils/getDiscourseNodeColors"; @@ -105,6 +106,7 @@ const DiscourseNodeColorSetting = ({ label="Color" description="Changes the color of tags and canvas nodes" scope="global" + settingKeys={[DISCOURSE_NODE_KEYS.canvasSettings, CANVAS_KEYS.color]} control={(controlId) => ( { ROAM_DOCS.creatingNodes, )} scope="global" + settingKeys={TEMPLATE_SETTING_KEYS} control={ diff --git a/apps/roam/src/components/settings/PreferencesGeneral.tsx b/apps/roam/src/components/settings/PreferencesGeneral.tsx index 43140e11b..94b5d1ccc 100644 --- a/apps/roam/src/components/settings/PreferencesGeneral.tsx +++ b/apps/roam/src/components/settings/PreferencesGeneral.tsx @@ -162,7 +162,7 @@ const PreferencesGeneral = ({ "Use stored relations instead of legacy pattern-based relations", ROAM_DOCS.migrationToStoredRelations, )} - settingKeys={["Reified relation triples"]} + settingKeys={[PERSONAL_KEYS.reifiedRelationTriples]} initialValue={personalSettings["Reified relation triples"]} value={storedRelations} onBeforeChange={async (checked) => { diff --git a/apps/roam/src/components/settings/Settings.tsx b/apps/roam/src/components/settings/Settings.tsx index b616354c1..88e5e96d3 100644 --- a/apps/roam/src/components/settings/Settings.tsx +++ b/apps/roam/src/components/settings/Settings.tsx @@ -33,7 +33,7 @@ import { getVersionWithDate } from "~/utils/getVersion"; import posthog from "posthog-js"; import { bulkReadSettings } from "./utils/accessors"; import { onSettingChange, settingKeys } from "./utils/settingsEmitter"; -import { SETTINGS_TAB_IDS } from "./utils/settingsTabs"; +import { SETTINGS_TAB_IDS, SETTINGS_TAB_META } from "./utils/settingsTabs"; import { resolveInitialSettingsPath, settingsNavReducer, @@ -193,7 +193,7 @@ export const SettingsDialog = ({ Preferences Features