Skip to content
Open
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
e7114b4
ENG-1465: Add "Use new settings store" feature flag (#811)
sid597 Feb 25, 2026
c324bb6
ENG-1455: Dual-read from old-system settings and from blockprops (#812)
sid597 Mar 1, 2026
700b590
ENG-1472: Refactor BlockPropSettingPanels to add accessor-backed defa…
sid597 Mar 1, 2026
b88f56a
Eng-1473 port non boolean personal setting consumers (#823)
sid597 Mar 1, 2026
4c908aa
ENG-1467: Port global setting consumer reads (→ getGlobalSetting) (#824)
sid597 Mar 1, 2026
47411c2
ENG-1456: Migrate personal boolean flag consumers (getSetting → getPe…
sid597 Mar 2, 2026
0bc1d22
ENG-1479: Port suggestive mode settings to dual-read (#845)
sid597 Mar 3, 2026
3b4c80a
ENG-1468: Port node and relations tree based consumers (#825)
sid597 Mar 3, 2026
f6cc8c2
ENG-1478: Port left sidebar to dual-readers + reactivity
sid597 Mar 2, 2026
5155a2c
ENG-1478: DRY merge helpers, real dual-read in settings panels, emitt…
sid597 Mar 2, 2026
686fd05
ENG-1478: refresh config tree before emitter-driven rebuild
sid597 Mar 3, 2026
eea529a
ENG-1478: extract emitter key constants, fix stale TODO
sid597 Mar 3, 2026
5925be2
ENG-1478: fix legacy adapter child.uid → child.text, fix settingKeys …
sid597 Mar 3, 2026
ee8bf56
ENG-1478: skip reload prompt when new settings store is active
sid597 Mar 3, 2026
ade239b
ENG-1478: guard getGlobalSetting/getPersonalSetting against empty keys
sid597 Mar 3, 2026
b7d26df
ENG-1469: Refactor getDiscourseRelations and getDiscourseNodes to rea…
sid597 Mar 3, 2026
a1ce922
Fix: use backedBy 'user' for initial discourse nodes in block props
sid597 Mar 4, 2026
915c0eb
Hardcode backedBy 'user' in toDiscourseNode to match legacy behavior
sid597 Mar 4, 2026
d682a86
ENG-1484: reactive settings for triggers and suggestive mode overlay
sid597 Mar 5, 2026
d280bfd
ENG-1484: use nullish coalescing for trigger fallbacks to match init …
sid597 Mar 6, 2026
2fdb3de
ENG-1484: reactive suggestive mode flag, gate reload prompts on new s…
sid597 Mar 6, 2026
bc261e0
ENG-1484: broad selector for suggestive overlay cleanup to match legacy
sid597 Mar 6, 2026
0ee94fa
ENG-1484: fix prettier formatting (root config)
sid597 Mar 6, 2026
dc1d46a
ENG-1484: drop reload prompts — reactivity is unconditional via dual-…
sid597 Mar 6, 2026
46fa9bc
Merge pull request #844 from DiscourseGraphs/eng-1478-port-both-globa…
sid597 Mar 9, 2026
6606ffa
Merge pull request #853 from DiscourseGraphs/eng-1469-refactor-getdis…
sid597 Mar 9, 2026
b5257d7
ENG-1499: Replace raw string accessor paths with shared constants fro…
sid597 Mar 9, 2026
5a462ce
Fix missed keyImageOption getter to use shared constants
sid597 Mar 9, 2026
fc58bf3
Merge pull request #875 from DiscourseGraphs/eng-1499-convert-legacy-…
sid597 Mar 10, 2026
cd16a66
ENG-1519: Add legacy-to-blockprops migration, remove backedBy from sc…
sid597 Mar 10, 2026
b97def3
Fix shouldWrite skipping after reconciliation, warn on missing person…
sid597 Mar 10, 2026
14e72c9
Add migration telemetry for legacy parse failures
sid597 Mar 10, 2026
def877f
Use backend query for node migration
sid597 Mar 10, 2026
cd2fa4c
ENG-735: Remove inline settings UI from config pages
sid597 Mar 10, 2026
f3cae70
Use Object.values instead of unused destructured key
sid597 Mar 11, 2026
991a7c9
ENG-1503: Replace getFormattedConfigTree consumers with direct helper…
sid597 Mar 5, 2026
3b67eec
ENG-1484: extract constants, remove dead defaults per review
sid597 Mar 11, 2026
bdbbbc4
Fix useConfig to call buildConfig() for dual-read merge
sid597 Mar 11, 2026
7f51464
merge migration-block-init-staging-branch
sid597 Mar 11, 2026
c44b338
prettier
sid597 Mar 11, 2026
cbf7345
Merge pull request #860 from DiscourseGraphs/eng-1503-remove-remnants…
sid597 Mar 11, 2026
028bec6
Merge migration-block-init-staging-branch into eng-735
sid597 Mar 11, 2026
6e28926
Fix import path for DISCOURSE_CONFIG_PAGE_TITLE
sid597 Mar 11, 2026
29f99fc
Merge branch 'migration-block-init-staging-branch' into eng-1519-lega…
sid597 Mar 11, 2026
f62b3f8
fix review
sid597 Mar 13, 2026
a1f1dc0
Merge pull request #876 from DiscourseGraphs/eng-1519-legacy-to-block…
sid597 Mar 13, 2026
8b23c58
Merge pull request #878 from DiscourseGraphs/eng-735-remove-ui-from-s…
sid597 Mar 13, 2026
fd332e9
Merge remote-tracking branch 'origin/migration-block-init-staging-bra…
sid597 Mar 13, 2026
4877527
Merge pull request #861 from DiscourseGraphs/eng-1484-reactive-settin…
sid597 Mar 13, 2026
19f90c0
Merge origin/main into migration-block-init-staging-branch
sid597 Mar 16, 2026
26fef42
Merge origin/main (ENG-1403, ENG-1508) into migration-block-init-stag…
sid597 Mar 16, 2026
b986299
ENG-1470: Fix dual-read gaps found during flag-ON validation (#896)
sid597 Apr 15, 2026
f437214
Merge origin/main into migration-block-init-staging-branch
sid597 Apr 17, 2026
53ac112
Fix stale startup snapshot: populate config tree before bulkReadSettings
sid597 Apr 17, 2026
7b38196
Fix toggleFoldedState race on rapid chevron clicks
sid597 Apr 17, 2026
079a629
Keep Query pages schema default at discourse-graph/queries/*
sid597 Apr 17, 2026
30f43b2
Include Reified relation triples in legacy personal settings
sid597 Apr 17, 2026
ea638a5
Close panel races between syncToBlock and refreshConfigTree
sid597 Apr 17, 2026
7b353b9
Report per-key mismatches in dgDualReadLog; fix legacy returnNode read
sid597 Apr 20, 2026
f224daa
Merge origin/main into migration-block-init-staging-branch
sid597 Apr 22, 2026
aa2eac6
ENG-1664: Replace getBlockUidByTextOnPage scan with pull in settings …
sid597 Apr 24, 2026
1290ca9
ENG-1659: Use typed canvasSettings.color keys on color picker writes …
sid597 Apr 24, 2026
d173c8e
ENG-1663 Await scratch/conditions/return creation before writing chil…
sid597 Apr 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions apps/roam/src/components/DiscourseContextOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { ContextContent } from "./DiscourseContext";
import useInViewport from "react-in-viewport/dist/es/lib/useInViewport";
import normalizePageTitle from "roamjs-components/queries/normalizePageTitle";
import deriveDiscourseNodeAttribute from "~/utils/deriveDiscourseNodeAttribute";
import getSettingValueFromTree from "roamjs-components/util/getSettingValueFromTree";
import getBasicTreeByParentUid from "roamjs-components/queries/getBasicTreeByParentUid";
import { getDiscourseNodeSetting } from "~/components/settings/utils/accessors";
import { DISCOURSE_NODE_KEYS } from "~/components/settings/utils/settingKeys";
import nanoid from "nanoid";
import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTitle";
import getDiscourseContextResults from "~/utils/getDiscourseContextResults";
Expand Down Expand Up @@ -180,11 +180,10 @@ const useDiscourseContext = (uid: string, tag: string) => {
.then(({ refs, results }) => {
const discourseNode = findDiscourseNode({ uid: uid });
if (discourseNode) {
const attribute = getSettingValueFromTree({
tree: getBasicTreeByParentUid(discourseNode.type),
key: "Overlay",
defaultValue: "Overlay",
});
const attribute =
getDiscourseNodeSetting<string>(discourseNode.type, [
DISCOURSE_NODE_KEYS.overlay,
]) || "Overlay";
return deriveDiscourseNodeAttribute({
uid: uid,
attribute,
Expand Down
4 changes: 3 additions & 1 deletion apps/roam/src/components/DiscourseFloatingMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
import { FeedbackWidget } from "./BirdEatsBugs";
import { render as renderSettings } from "~/components/settings/Settings";
import posthog from "posthog-js";
import { getPersonalSetting } from "./settings/utils/accessors";
import { PERSONAL_KEYS } from "./settings/utils/settingKeys";

type DiscourseFloatingMenuProps = {
// CSS placement class
Expand Down Expand Up @@ -128,7 +130,7 @@ export const installDiscourseFloatingMenu = (
floatingMenuAnchor.id = ANCHOR_ID;
document.getElementById("app")?.appendChild(floatingMenuAnchor);
}
if (onLoadArgs.extensionAPI.settings.get("hide-feedback-button") as boolean) {
if (getPersonalSetting<boolean>([PERSONAL_KEYS.hideFeedbackButton])) {
floatingMenuAnchor.classList.add("hidden");
}
ReactDOM.render(
Expand Down
19 changes: 13 additions & 6 deletions apps/roam/src/components/DiscourseNodeMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,11 @@ import { getNewDiscourseNodeText } from "~/utils/formatUtils";
import { OnloadArgs } from "roamjs-components/types";
import { formatHexColor } from "./settings/DiscourseNodeCanvasSettings";
import posthog from "posthog-js";
import { setPersonalSetting } from "~/components/settings/utils/accessors";
import {
getPersonalSetting,
setPersonalSetting,
} from "~/components/settings/utils/accessors";
import { PERSONAL_KEYS } from "~/components/settings/utils/settingKeys";

type Props = {
textarea?: HTMLTextAreaElement;
Expand Down Expand Up @@ -423,9 +427,12 @@ export const NodeMenuTriggerComponent = ({
const [isActive, setIsActive] = useState(false);
const [comboKey, setComboKey] = useState<IKeyCombo>(
() =>
(extensionAPI.settings.get(
"personal-node-menu-trigger",
) as IKeyCombo) || { modifiers: 0, key: "" },
getPersonalSetting<IKeyCombo>([
PERSONAL_KEYS.personalNodeMenuTrigger,
]) || {
modifiers: 0,
key: "",
},
);

const handleKeyDown = useCallback(
Expand All @@ -438,7 +445,7 @@ export const NodeMenuTriggerComponent = ({
const combo = { key: comboObj.key, modifiers: comboObj.modifiers };
setComboKey(combo);
void extensionAPI.settings.set("personal-node-menu-trigger", combo);
setPersonalSetting(["Personal node menu trigger"], combo);
setPersonalSetting([PERSONAL_KEYS.personalNodeMenuTrigger], combo);
},
[extensionAPI],
);
Expand All @@ -460,7 +467,7 @@ export const NodeMenuTriggerComponent = ({
onClick={() => {
setComboKey({ modifiers: 0, key: "" });
void extensionAPI.settings.set("personal-node-menu-trigger", "");
setPersonalSetting(["Personal node menu trigger"], "");
setPersonalSetting([PERSONAL_KEYS.personalNodeMenuTrigger], "");
}}
minimal
/>
Expand Down
11 changes: 7 additions & 4 deletions apps/roam/src/components/DiscourseNodeSearchMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,12 @@ import { OnloadArgs } from "roamjs-components/types";
import getDiscourseNodes, { DiscourseNode } from "~/utils/getDiscourseNodes";
import getDiscourseNodeFormatExpression from "~/utils/getDiscourseNodeFormatExpression";
import { Result } from "~/utils/types";
import { getSetting } from "~/utils/extensionSettings";
import MiniSearch from "minisearch";
import { setPersonalSetting } from "~/components/settings/utils/accessors";
import {
getPersonalSetting,
setPersonalSetting,
} from "~/components/settings/utils/accessors";
import { PERSONAL_KEYS } from "~/components/settings/utils/settingKeys";

type Props = {
textarea: HTMLTextAreaElement;
Expand Down Expand Up @@ -711,7 +714,7 @@ export const NodeSearchMenuTriggerSetting = ({
}) => {
const extensionAPI = onloadArgs.extensionAPI;
const [nodeSearchTrigger, setNodeSearchTrigger] = useState<string>(
getSetting("node-search-trigger", "@"),
getPersonalSetting<string>([PERSONAL_KEYS.nodeSearchMenuTrigger]) ?? "@",
);

const handleNodeSearchTriggerChange = (
Expand All @@ -726,7 +729,7 @@ export const NodeSearchMenuTriggerSetting = ({

setNodeSearchTrigger(trigger);
void extensionAPI.settings.set("node-search-trigger", trigger);
setPersonalSetting(["Node search menu trigger"], trigger);
setPersonalSetting([PERSONAL_KEYS.nodeSearchMenuTrigger], trigger);
};
return (
<InputGroup
Expand Down
123 changes: 106 additions & 17 deletions apps/roam/src/components/LeftSidebarView.tsx
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,33 @@ import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTit
import openBlockInSidebar from "roamjs-components/writes/openBlockInSidebar";
import extractRef from "roamjs-components/util/extractRef";
import {
getFormattedConfigTree,
notify,
subscribe,
} from "~/utils/discourseConfigRef";
import type {
LeftSidebarConfig,
LeftSidebarPersonalSectionConfig,
onSettingChange,
settingKeys,
} from "~/components/settings/utils/settingsEmitter";
import {
type LeftSidebarConfig,
type LeftSidebarPersonalSectionConfig,
mergeGlobalSectionWithAccessor,
mergePersonalSectionsWithAccessor,
} from "~/utils/getLeftSidebarSettings";
import discourseConfigRef, { notify } from "~/utils/discourseConfigRef";
import { getLeftSidebarSettings } from "~/utils/getLeftSidebarSettings";
import {
getGlobalSetting,
getPersonalSetting,
setGlobalSetting,
setPersonalSetting,
} from "~/components/settings/utils/accessors";
import {
PERSONAL_KEYS,
GLOBAL_KEYS,
LEFT_SIDEBAR_KEYS,
LEFT_SIDEBAR_SETTINGS_KEYS,
} from "~/components/settings/utils/settingKeys";
import type {
LeftSidebarGlobalSettings,
PersonalSection,
} from "~/components/settings/utils/zodSchema";
import { createBlock } from "roamjs-components/writes";
import deleteBlock from "roamjs-components/writes/deleteBlock";
import getTextByBlockUid from "roamjs-components/queries/getTextByBlockUid";
Expand All @@ -39,11 +58,14 @@ import { SettingsDialog } from "./settings/Settings";
import { OnloadArgs } from "roamjs-components/types";
import renderOverlay from "roamjs-components/util/renderOverlay";
import getBasicTreeByParentUid from "roamjs-components/queries/getBasicTreeByParentUid";
import { DISCOURSE_CONFIG_PAGE_TITLE } from "~/utils/renderNodeConfigPage";
import { DISCOURSE_CONFIG_PAGE_TITLE } from "~/data/constants";
import getPageTitleByPageUid from "roamjs-components/queries/getPageTitleByPageUid";
import { migrateLeftSidebarSettings } from "~/utils/migrateLeftSidebarSettings";
import posthog from "posthog-js";

const getCurrentLeftSidebarConfig = (): LeftSidebarConfig =>
getLeftSidebarSettings(discourseConfigRef.tree);

const parseReference = (text: string) => {
const extracted = extractRef(text);
if (text.startsWith("((") && text.endsWith("))")) {
Expand Down Expand Up @@ -95,12 +117,18 @@ const toggleFoldedState = ({
setIsOpen,
folded,
parentUid,
isGlobal,
sectionIndex,
}: {
isOpen: boolean;
setIsOpen: Dispatch<SetStateAction<boolean>>;
folded: { uid?: string; value: boolean };
parentUid: string;
isGlobal?: boolean;
sectionIndex?: number;
}) => {
const newFolded = !isOpen;

if (isOpen) {
setIsOpen(false);
if (folded.uid) {
Expand All @@ -118,6 +146,24 @@ const toggleFoldedState = ({
folded.uid = newUid;
folded.value = true;
}

if (isGlobal) {
setGlobalSetting(
[
GLOBAL_KEYS.leftSidebar,
LEFT_SIDEBAR_KEYS.settings,
LEFT_SIDEBAR_SETTINGS_KEYS.folded,
],
newFolded,
);
} else if (sectionIndex !== undefined) {
const sections =
getPersonalSetting<PersonalSection[]>([PERSONAL_KEYS.leftSidebar]) || [];
if (sections[sectionIndex]) {
sections[sectionIndex].Settings.Folded = newFolded;
setPersonalSetting([PERSONAL_KEYS.leftSidebar], sections);
}
Comment thread
devin-ai-integration[bot] marked this conversation as resolved.
}
};

const SectionChildren = ({
Expand Down Expand Up @@ -160,8 +206,10 @@ const SectionChildren = ({

const PersonalSectionItem = ({
section,
sectionIndex,
}: {
section: LeftSidebarPersonalSectionConfig;
sectionIndex: number;
}) => {
const titleRef = parseReference(section.text);
const blockText = useMemo(
Expand All @@ -182,6 +230,7 @@ const PersonalSectionItem = ({
setIsOpen,
folded: section.settings.folded,
parentUid: section.settings.uid || "",
sectionIndex,
});
};

Expand Down Expand Up @@ -226,9 +275,9 @@ const PersonalSections = ({ config }: { config: LeftSidebarConfig }) => {

return (
<div className="personal-left-sidebar-sections">
{sections.map((section) => (
{sections.map((section, index) => (
<div key={section.uid}>
<PersonalSectionItem section={section} />
<PersonalSectionItem section={section} sectionIndex={index} />
</div>
))}
</div>
Expand All @@ -253,6 +302,7 @@ const GlobalSection = ({ config }: { config: LeftSidebarConfig["global"] }) => {
setIsOpen,
folded: config.settings.folded,
parentUid: config.settings.uid,
isGlobal: true,
});
}}
>
Expand All @@ -276,22 +326,61 @@ const GlobalSection = ({ config }: { config: LeftSidebarConfig["global"] }) => {
);
};

// TODO(ENG-1471): Remove old-system merge when migration complete — just use accessor values directly.
// See mergeGlobalSectionWithAccessor/mergePersonalSectionsWithAccessor for why the merge exists.
const buildConfig = (): LeftSidebarConfig => {
// Read VALUES from accessor (handles flag routing + mismatch detection)
const globalValues = getGlobalSetting<LeftSidebarGlobalSettings>([
GLOBAL_KEYS.leftSidebar,
]);
const personalValues = getPersonalSetting<PersonalSection[]>([
PERSONAL_KEYS.leftSidebar,
]);

// Read UIDs from old system (needed for fold CRUD during dual-write)
const oldConfig = getCurrentLeftSidebarConfig();

return {
uid: oldConfig.uid,
favoritesMigrated: oldConfig.favoritesMigrated,
sidebarMigrated: oldConfig.sidebarMigrated,
global: mergeGlobalSectionWithAccessor(oldConfig.global, globalValues),
personal: {
uid: oldConfig.personal.uid,
sections: mergePersonalSectionsWithAccessor(
oldConfig.personal.sections,
personalValues,
),
},
allPersonalSections: oldConfig.allPersonalSections,
};
};

export const useConfig = () => {
const [config, setConfig] = useState(
() => getFormattedConfigTree().leftSidebar,
);
const [config, setConfig] = useState(() => buildConfig());
useEffect(() => {
const handleUpdate = () => {
setConfig(getFormattedConfigTree().leftSidebar);
setConfig(buildConfig());
};
const unsubscribe = subscribe(handleUpdate);
const unsubGlobal = onSettingChange(
settingKeys.globalLeftSidebar,
handleUpdate,
);
const unsubPersonal = onSettingChange(
settingKeys.personalLeftSidebar,
handleUpdate,
);
return () => {
unsubscribe();
unsubGlobal();
unsubPersonal();
};
}, []);
return { config, setConfig };
};

// TODO(ENG-1471): refreshAndNotify still needed by settings panels
// (LeftSidebarGlobalSettings, LeftSidebarPersonalSettings) for old-system CRUD.
// Remove when settings panels also read via accessors + emitter.
export const refreshAndNotify = () => {
refreshConfigTree();
notify();
Expand Down Expand Up @@ -420,7 +509,7 @@ const LeftSidebarView = ({ onloadArgs }: { onloadArgs: OnloadArgs }) => {
};

const migrateFavorites = async () => {
const config = getFormattedConfigTree().leftSidebar;
const config = getCurrentLeftSidebarConfig();

if (config.favoritesMigrated.value) return;

Expand Down
5 changes: 1 addition & 4 deletions apps/roam/src/components/ModifyNodeDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,7 @@ const ModifyNodeDialog = ({
}

if (keyImageOption === "query-builder") {
const parentUid = resolveQueryBuilderRef({
queryRef: qbAlias,
extensionAPI,
});
const parentUid = resolveQueryBuilderRef({ queryRef: qbAlias });
const results = await runQuery({
extensionAPI,
parentUid,
Expand Down
8 changes: 7 additions & 1 deletion apps/roam/src/components/QueryEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ import { ALL_SELECTION_SUGGESTIONS } from "~/utils/predefinedSelections";
import { getAlias } from "~/utils/parseResultSettings";
import { setDiscourseNodeSetting } from "~/components/settings/utils/accessors";
import { IndexSchema } from "~/components/settings/utils/zodSchema";
import {
DISCOURSE_NODE_KEYS,
SPECIFICATION_KEYS,
} from "~/components/settings/utils/settingKeys";

const getSourceCandidates = (cs: Condition[]): string[] =>
cs.flatMap((c) =>
Expand Down Expand Up @@ -513,7 +517,9 @@ const QueryEditor: QueryEditorComponent = ({
}

const path =
settingKey === "index" ? ["index"] : ["specification", "query"];
settingKey === "index"
? [DISCOURSE_NODE_KEYS.index]
: [DISCOURSE_NODE_KEYS.specification, SPECIFICATION_KEYS.query];

window.clearTimeout(blockPropSyncTimeoutRef.current);
blockPropSyncTimeoutRef.current = window.setTimeout(() => {
Expand Down
Loading