From 69ca8c1d5f028f5c168e297722f8a50aaef1c321 Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Mon, 31 Aug 2026 11:27:14 -0400 Subject: [PATCH 1/6] ENG-2185 Move export settings out of Settings into the Export panel The 7 export controls were used by exactly one surface but lived in Settings, so changing an option meant leaving the Export flow, opening Settings, and coming back. Move them into a collapsed "Export options" disclosure at the bottom of the Export dialog's Export tab, and remove the Advanced > Export tab that ENG-2186 parked in Settings for this change to take out. The controls, their storage keys, and the read path in getExportSettings are unchanged, so saved values persist and export behaviour is untouched. The advancedExport tab id goes with it, and the saved "discourse-graph-export" deep link now resolves to the default tab instead of a tab that no longer renders. Co-Authored-By: Claude Opus 5 --- apps/roam/src/components/Export.tsx | 66 +++++++++++++++---- .../ExportSettings.tsx => ExportOptions.tsx} | 10 +-- .../roam/src/components/settings/Settings.tsx | 9 --- .../components/settings/utils/settingsTabs.ts | 4 +- .../roam/guides/sharing-discourse-graph.md | 2 +- 5 files changed, 63 insertions(+), 28 deletions(-) rename apps/roam/src/components/{settings/ExportSettings.tsx => ExportOptions.tsx} (94%) diff --git a/apps/roam/src/components/Export.tsx b/apps/roam/src/components/Export.tsx index 0f1c350fb..4941b4dd6 100644 --- a/apps/roam/src/components/Export.tsx +++ b/apps/roam/src/components/Export.tsx @@ -15,8 +15,9 @@ import { RadioGroup, Radio, FormGroup, + Collapse, } from "@blueprintjs/core"; -import React, { useState, useEffect, useMemo, FormEvent } from "react"; +import React, { useState, useEffect, useMemo, useRef, FormEvent } from "react"; import MenuItemSelect from "roamjs-components/components/MenuItemSelect"; import { saveAs } from "file-saver"; import { Result } from "roamjs-components/types/query-builder"; @@ -91,7 +92,12 @@ import { type NodeUidWithType, } from "~/utils/publishNodesToGroups"; import { getLoggedInClient, getSupabaseContext } from "~/utils/supabaseContext"; -import { isNodeSharingEnabled } from "~/components/settings/utils/accessors"; +import { + bulkReadSettings, + isNodeSharingEnabled, +} from "~/components/settings/utils/accessors"; +import refreshConfigTree from "~/utils/refreshConfigTree"; +import ExportOptions from "./ExportOptions"; const ExportProgress = ({ id }: { id: string }) => { const [progress, setProgress] = useState(0); @@ -225,6 +231,22 @@ const ExportDialog: ExportDialogComponent = ({ if (initialPanel) setSelectedTabId(INITIAL_PANEL_TO_TAB_ID[initialPanel]); }, [initialPanel, sharingEnabled]); const [includeDiscourseContext, setIncludeDiscourseContext] = useState(false); + const [exportOptionsOpen, setExportOptionsOpen] = useState(false); + const exportOptionsOpened = useRef(false); + const exportGlobalSettings = useMemo( + () => bulkReadSettings().globalSettings, + [], + ); + + // The export option panels write legacy config blocks alongside block props, so + // the cached config tree has to be refreshed the way SettingsDialog does. Gated + // on the section having been opened because refreshConfigTree re-reads every + // node page and re-registers the datalog translators, which is too heavy to run + // on every close of a dialog that is opened for each export. + const closeDialog = (): void => { + if (exportOptionsOpened.current) refreshConfigTree(); + onClose(); + }; const [gitHubAccessToken, setGitHubAccessToken] = useState( getSetting("oauth-github", null), ); @@ -756,7 +778,7 @@ const ExportDialog: ExportDialogComponent = ({ }); } finally { setLoading(false); - onClose(); + closeDialog(); } }; @@ -796,7 +818,7 @@ const ExportDialog: ExportDialogComponent = ({ fileCount: files.length, }); } - onClose(); + closeDialog(); } catch (e) { setError("Failed to export files."); posthog.capture("Export Dialog: Export Failed", { @@ -888,7 +910,7 @@ const ExportDialog: ExportDialogComponent = ({ : "success", id: "query-builder-publish-success", }); - if (hasPublishedNodes) onClose(); + if (hasPublishedNodes) closeDialog(); } catch (e) { internalError({ error: e as Error, @@ -981,11 +1003,33 @@ const ExportDialog: ExportDialogComponent = ({ + +
+
{error} -
- -
-
+ +
+ +
+
From 04e314e010b04ffbfe50aa39262def5b11d5b0a1 Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Sat, 5 Sep 2026 19:12:07 -0400 Subject: [PATCH 5/6] ENG-2185 Discourse context on its own line above the export options row Review feedback: the checkbox sits right-aligned on the line above; the line below is the Export options toggle on the left and the result count on the right. Co-Authored-By: Claude Fable 5.1 --- apps/roam/src/components/Export.tsx | 58 ++++++++++++++--------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/apps/roam/src/components/Export.tsx b/apps/roam/src/components/Export.tsx index 6d76d657b..e8ab30a57 100644 --- a/apps/roam/src/components/Export.tsx +++ b/apps/roam/src/components/Export.tsx @@ -978,7 +978,30 @@ const ExportDialog: ExportDialogComponent = ({ /> -
+
+ + { + setIncludeDiscourseContext( + (e.target as HTMLInputElement).checked, + ); + }} + labelElement={ + + Discourse context + + } + /> + +
+
From d29883b7fac61744926acddd7ae979d4b8f7853f Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Mon, 7 Sep 2026 18:20:13 -0400 Subject: [PATCH 6/6] ENG-2185 Drop unit tests and trim comments to the decisions they record Co-Authored-By: Claude Fable 5.1 --- apps/roam/src/components/Export.tsx | 18 +++++------------- .../components/settings/utils/settingsTabs.ts | 2 +- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/apps/roam/src/components/Export.tsx b/apps/roam/src/components/Export.tsx index e8ab30a57..6d96250ac 100644 --- a/apps/roam/src/components/Export.tsx +++ b/apps/roam/src/components/Export.tsx @@ -234,20 +234,15 @@ const ExportDialog: ExportDialogComponent = ({ const [includeDiscourseContext, setIncludeDiscourseContext] = useState(false); const [exportOptionsOpen, setExportOptionsOpen] = useState(false); const exportOptionsOpened = useRef(false); - // Re-read on every open rather than once at mount: Collapse unmounts the option - // panels while closed, so each open seeds them from the current stored values - // instead of whatever the dialog saw when it first rendered. + // Collapse unmounts the panels while closed, so each open seeds them from current values. const exportGlobalSettings = useMemo( () => bulkReadSettings().globalSettings, // eslint-disable-next-line react-hooks/exhaustive-deps [exportOptionsOpen], ); - // The export option panels write legacy config blocks alongside block props, so - // the cached config tree has to be refreshed the way SettingsDialog does. Gated - // on the section having been opened because refreshConfigTree re-reads every - // node page and re-registers the datalog translators, which is too heavy to run - // on every close of a dialog that is opened for each export. + // Option panels also write legacy config blocks, so refresh the tree as SettingsDialog + // does, but only if they were opened: refreshConfigTree re-reads every node page. const closeDialog = (): void => { if (exportOptionsOpened.current) refreshConfigTree(); onClose(); @@ -1055,11 +1050,8 @@ const ExportDialog: ExportDialogComponent = ({ // eslint-disable-next-line @typescript-eslint/no-misused-promises setTimeout(async () => { try { - // The export reads settings inside its callback, and the number - // and select panels defer their write behind a short timer. The - // await matters as much as the flush: committing only starts the - // Roam block update, so an option edited a moment ago would - // otherwise still read as its previous value here. + // Awaited, not just fired: a commit only starts the Roam update, and an option edited + // a moment ago would otherwise still read as its previous value here. await flushPendingSettingWrites(); const exportType = exportTypes.find( (e) => e.name === activeExportType, diff --git a/apps/roam/src/components/settings/utils/settingsTabs.ts b/apps/roam/src/components/settings/utils/settingsTabs.ts index 77a23f94f..0c0e8e163 100644 --- a/apps/roam/src/components/settings/utils/settingsTabs.ts +++ b/apps/roam/src/components/settings/utils/settingsTabs.ts @@ -23,7 +23,7 @@ export const SETTINGS_TAB_ALIASES: Record = { "canvas-shortcuts-personal-settings": SETTINGS_TAB_IDS.featuresCanvas, "left-sidebar-personal-settings": SETTINGS_TAB_IDS.featuresLeftSidebar, "left-sidebar-global-settings": SETTINGS_TAB_IDS.featuresLeftSidebar, - // Export options left Settings in ENG-2185; they now live in the Export dialog. + // Export options now live in the Export dialog. "discourse-graph-export": SETTINGS_TAB_IDS.preferencesGeneral, "discourse-nodes": SETTINGS_TAB_IDS.grammarNodes, "discourse-relations": SETTINGS_TAB_IDS.grammarRelations,