From 12a8e441244b141eea390669baf1a094835faecf Mon Sep 17 00:00:00 2001 From: sid597 Date: Mon, 5 Jan 2026 01:13:12 +0530 Subject: [PATCH 1/2] ENG-1189: Prod: Init block prop based schema --- .../settings/data/blockPropsSettingsConfig.ts | 19 + .../src/components/settings/utils/init.ts | 336 ++++++++++++++++++ 2 files changed, 355 insertions(+) create mode 100644 apps/roam/src/components/settings/data/blockPropsSettingsConfig.ts create mode 100644 apps/roam/src/components/settings/utils/init.ts diff --git a/apps/roam/src/components/settings/data/blockPropsSettingsConfig.ts b/apps/roam/src/components/settings/data/blockPropsSettingsConfig.ts new file mode 100644 index 000000000..e79c88475 --- /dev/null +++ b/apps/roam/src/components/settings/data/blockPropsSettingsConfig.ts @@ -0,0 +1,19 @@ +/* eslint-disable @typescript-eslint/naming-convention */ + +export const DG_BLOCK_PROP_SETTINGS_PAGE_TITLE = + "roam/js/discourse-graph/block-prop-settings"; + +export const DISCOURSE_NODE_PAGE_PREFIX = "discourse-graph/nodes/"; + +export const TOP_LEVEL_BLOCK_PROP_KEYS = { + featureFlags: "Feature Flags", + global: "Global", +} as const; + +export const DISCOURSE_NODE_BLOCK_KEYS = { + template: "Template", + index: "Index", + specification: "Specification", +} as const; + +/* eslint-enable @typescript-eslint/naming-convention */ diff --git a/apps/roam/src/components/settings/utils/init.ts b/apps/roam/src/components/settings/utils/init.ts new file mode 100644 index 000000000..d96f0445b --- /dev/null +++ b/apps/roam/src/components/settings/utils/init.ts @@ -0,0 +1,336 @@ +import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTitle"; +import getShallowTreeByParentUid from "roamjs-components/queries/getShallowTreeByParentUid"; +import { createPage, createBlock } from "roamjs-components/writes"; +import setBlockProps from "~/utils/setBlockProps"; +import getBlockProps, { type json } from "~/utils/getBlockProps"; +// eslint-disable-next-line @typescript-eslint/naming-convention +import INITIAL_NODE_VALUES from "~/data/defaultDiscourseNodes"; +import { + DiscourseNodeSchema, + FeatureFlagsSchema, + GlobalSettingsSchema, + PersonalSettingsSchema, +} from "./zodSchema"; +import type { ZodSchema } from "zod"; +import { + DG_BLOCK_PROP_SETTINGS_PAGE_TITLE, + DISCOURSE_NODE_PAGE_PREFIX, + TOP_LEVEL_BLOCK_PROP_KEYS, + DISCOURSE_NODE_BLOCK_KEYS, +} from "../data/blockPropsSettingsConfig"; + +let cachedPersonalSettingsKey: string | null = null; + +const getPersonalSettingsKey = (): string => { + if (cachedPersonalSettingsKey !== null) { + return cachedPersonalSettingsKey; + } + cachedPersonalSettingsKey = window.roamAlphaAPI.user.uid() || ""; + return cachedPersonalSettingsKey; +}; + +const getDiscourseNodePageTitle = (nodeLabel: string): string => { + return `${DISCOURSE_NODE_PAGE_PREFIX}${nodeLabel}`; +}; + +const ensurePageExists = async (pageTitle: string): Promise => { + let pageUid = getPageUidByPageTitle(pageTitle); + + if (!pageUid) { + pageUid = window.roamAlphaAPI.util.generateUID(); + await createPage({ + title: pageTitle, + uid: pageUid, + }); + } + + return pageUid; +}; + +const ensureBlocksExist = async ( + pageUid: string, + blockTexts: string[], + existingBlockMap: Record, +): Promise> => { + const missingBlocks = blockTexts.filter( + (blockText) => !existingBlockMap[blockText], + ); + + if (missingBlocks.length > 0) { + const createdBlocks = await Promise.all( + missingBlocks.map(async (blockText) => { + const uid = await createBlock({ + parentUid: pageUid, + node: { text: blockText }, + }); + return { text: blockText, uid }; + }), + ); + + createdBlocks.forEach((block) => { + existingBlockMap[block.text] = block.uid; + }); + } + + return existingBlockMap; +}; + +const ensurePersonalBlockExists = async ( + pageUid: string, + existingBlockMap: Record, +): Promise<{ key: string; uid: string }> => { + const personalKey = getPersonalSettingsKey(); + + if (existingBlockMap[personalKey]) { + return { key: personalKey, uid: existingBlockMap[personalKey] }; + } + + const uid = await createBlock({ + parentUid: pageUid, + node: { text: personalKey }, + }); + + return { key: personalKey, uid }; +}; + +const buildBlockMap = (pageUid: string): Record => { + const existingChildren = getShallowTreeByParentUid(pageUid); + const blockMap: Record = {}; + existingChildren.forEach((child) => { + blockMap[child.text] = child.uid; + }); + return blockMap; +}; + +const initBlockPropsIfEmpty = (uid: string, schema: ZodSchema): void => { + const existingProps = getBlockProps(uid); + if (!existingProps || Object.keys(existingProps).length === 0) { + const defaults = schema.parse({}) as Record; + setBlockProps(uid, defaults, false); + } +}; + +const initializeSettingsBlockProps = ( + blockMap: Record, +): void => { + const configs = [ + { key: TOP_LEVEL_BLOCK_PROP_KEYS.featureFlags, schema: FeatureFlagsSchema }, + { key: TOP_LEVEL_BLOCK_PROP_KEYS.global, schema: GlobalSettingsSchema }, + { key: getPersonalSettingsKey(), schema: PersonalSettingsSchema }, + ]; + + for (const { key, schema } of configs) { + const uid = blockMap[key]; + if (uid) { + initBlockPropsIfEmpty(uid, schema); + } + } +}; + +const initSettingsPageBlocks = async (): Promise> => { + const pageUid = await ensurePageExists(DG_BLOCK_PROP_SETTINGS_PAGE_TITLE); + const blockMap = buildBlockMap(pageUid); + + const topLevelBlocks = Object.values(TOP_LEVEL_BLOCK_PROP_KEYS); + await ensureBlocksExist(pageUid, topLevelBlocks, blockMap); + + const personalBlock = await ensurePersonalBlockExists(pageUid, blockMap); + blockMap[personalBlock.key] = personalBlock.uid; + + initializeSettingsBlockProps(blockMap); + + return blockMap; +}; + +const ensureDiscourseNodePageExists = async ( + nodeLabel: string, +): Promise => { + const pageTitle = getDiscourseNodePageTitle(nodeLabel); + return ensurePageExists(pageTitle); +}; + +const initSingleDiscourseNode = async ( + node: (typeof INITIAL_NODE_VALUES)[number], +): Promise<{ label: string; pageUid: string } | null> => { + if (!node.text) return null; + + const pageUid = await ensureDiscourseNodePageExists(node.text); + const existingProps = getBlockProps(pageUid); + const blockMap = buildBlockMap(pageUid); + + for (const key of Object.values(DISCOURSE_NODE_BLOCK_KEYS)) { + if (!blockMap[key]) { + blockMap[key] = await createBlock({ + parentUid: pageUid, + node: { text: key }, + }); + } + } + + const templateUid = blockMap[DISCOURSE_NODE_BLOCK_KEYS.template]; + const indexUid = blockMap[DISCOURSE_NODE_BLOCK_KEYS.index]; + const specificationUid = blockMap[DISCOURSE_NODE_BLOCK_KEYS.specification]; + + if (!existingProps || Object.keys(existingProps).length === 0) { + const nodeData = DiscourseNodeSchema.parse({ + text: node.text, + type: node.type, + format: node.format || "", + shortcut: node.shortcut || "", + tag: node.tag || "", + graphOverview: node.graphOverview ?? false, + canvasSettings: node.canvasSettings || {}, + templateUid, + indexUid, + specificationUid, + backedBy: "user", + }); + + setBlockProps(pageUid, nodeData as Record, false); + } else if ( + !existingProps.templateUid || + !existingProps.indexUid || + !existingProps.specificationUid + ) { + setBlockProps(pageUid, { templateUid, indexUid, specificationUid }, true); + } + + return { label: node.text, pageUid }; +}; + +const initDiscourseNodePages = async (): Promise> => { + const results = await Promise.all( + INITIAL_NODE_VALUES.map((node) => initSingleDiscourseNode(node)), + ); + + const nodePageUids: Record = {}; + for (const result of results) { + if (result) { + nodePageUids[result.label] = result.pageUid; + } + } + + return nodePageUids; +}; + +const printAllSettings = (blockMap: Record): void => { + const featureFlagsUid = blockMap[TOP_LEVEL_BLOCK_PROP_KEYS.featureFlags]; + const globalUid = blockMap[TOP_LEVEL_BLOCK_PROP_KEYS.global]; + const personalKey = getPersonalSettingsKey(); + const personalUid = blockMap[personalKey]; + + const featureFlags = featureFlagsUid ? getBlockProps(featureFlagsUid) : null; + const globalSettings = globalUid ? getBlockProps(globalUid) : null; + const personalSettings = personalUid ? getBlockProps(personalUid) : null; + + console.group("🔧 Discourse Graph Settings Initialized"); + + if (featureFlags) { + console.group("🚩 Feature Flags"); + console.table(featureFlags); + console.groupEnd(); + } + + if (globalSettings) { + console.group("🌍 Global Settings"); + console.log("Trigger:", globalSettings?.Trigger || "(empty)"); + console.log( + "Canvas Page Format:", + globalSettings?.["Canvas Page Format"] || "(empty)", + ); + + if (globalSettings?.["Left Sidebar"]) { + console.group("📂 Left Sidebar"); + console.log(globalSettings["Left Sidebar"]); + console.groupEnd(); + } + + if (globalSettings?.Export) { + console.group("📤 Export Settings"); + console.table(globalSettings.Export); + console.groupEnd(); + } + + if (globalSettings?.["Suggestive Mode"]) { + console.group("💡 Suggestive Mode"); + console.log(globalSettings["Suggestive Mode"]); + console.groupEnd(); + } + + console.groupEnd(); + } + + if (personalSettings) { + console.group("👤 Personal Settings"); + console.log( + "Personal Node Menu Trigger:", + personalSettings?.["Personal Node Menu Trigger"] || "(empty)", + ); + console.log( + "Node Search Menu Trigger:", + personalSettings?.["Node Search Menu Trigger"] || "(empty)", + ); + console.log( + "Discourse Tool Shortcut:", + personalSettings?.["Discourse Tool Shortcut"] || "(empty)", + ); + + console.group("🎛️ Toggles"); + const toggles = { + // eslint-disable-next-line @typescript-eslint/naming-convention + "Discourse Context Overlay": + personalSettings?.["Discourse Context Overlay"], + // eslint-disable-next-line @typescript-eslint/naming-convention + "Suggestive Mode Overlay": personalSettings?.["Suggestive Mode Overlay"], + // eslint-disable-next-line @typescript-eslint/naming-convention + "Overlay in Canvas": personalSettings?.["Overlay in Canvas"], + // eslint-disable-next-line @typescript-eslint/naming-convention + "Text Selection Popup": personalSettings?.["Text Selection Popup"], + // eslint-disable-next-line @typescript-eslint/naming-convention + "Disable Sidebar Open": personalSettings?.["Disable Sidebar Open"], + // eslint-disable-next-line @typescript-eslint/naming-convention + "Page Preview": personalSettings?.["Page Preview"], + // eslint-disable-next-line @typescript-eslint/naming-convention + "Hide Feedback Button": personalSettings?.["Hide Feedback Button"], + // eslint-disable-next-line @typescript-eslint/naming-convention + "Streamline Styling": personalSettings?.["Streamline Styling"], + // eslint-disable-next-line @typescript-eslint/naming-convention + "Auto Canvas Relations": personalSettings?.["Auto Canvas Relations"], + // eslint-disable-next-line @typescript-eslint/naming-convention + "Disable Product Diagnostics": + personalSettings?.["Disable Product Diagnostics"], + }; + console.table(toggles); + console.groupEnd(); + + if (personalSettings?.["Left Sidebar"]) { + console.group("📂 Personal Left Sidebar"); + console.log(personalSettings["Left Sidebar"]); + console.groupEnd(); + } + + if (personalSettings?.Query) { + console.group("🔍 Query Settings"); + console.table(personalSettings.Query); + console.groupEnd(); + } + + console.groupEnd(); + } + + console.groupEnd(); +}; + +export type InitSchemaResult = { + blockUids: Record; + nodePageUids: Record; +}; + +export const initSchema = async (): Promise => { + const blockUids = await initSettingsPageBlocks(); + const nodePageUids = await initDiscourseNodePages(); + + printAllSettings(blockUids); + + return { blockUids, nodePageUids }; +}; From 8a707cf6d909ee6ea50f138ff5b6fea7807f6a97 Mon Sep 17 00:00:00 2001 From: sid597 Date: Mon, 12 Jan 2026 01:24:21 +0530 Subject: [PATCH 2/2] don't need this old relic --- .../settings/data/blockPropsSettingsConfig.ts | 19 -- .../data/defaultRelationsBlockProps.ts | 142 ++++++++++ .../src/components/settings/utils/init.ts | 258 +++++------------- .../settings/utils/zodSchema.example.ts | 18 +- .../components/settings/utils/zodSchema.ts | 34 ++- apps/roam/src/index.ts | 3 + 6 files changed, 257 insertions(+), 217 deletions(-) delete mode 100644 apps/roam/src/components/settings/data/blockPropsSettingsConfig.ts create mode 100644 apps/roam/src/components/settings/data/defaultRelationsBlockProps.ts diff --git a/apps/roam/src/components/settings/data/blockPropsSettingsConfig.ts b/apps/roam/src/components/settings/data/blockPropsSettingsConfig.ts deleted file mode 100644 index e79c88475..000000000 --- a/apps/roam/src/components/settings/data/blockPropsSettingsConfig.ts +++ /dev/null @@ -1,19 +0,0 @@ -/* eslint-disable @typescript-eslint/naming-convention */ - -export const DG_BLOCK_PROP_SETTINGS_PAGE_TITLE = - "roam/js/discourse-graph/block-prop-settings"; - -export const DISCOURSE_NODE_PAGE_PREFIX = "discourse-graph/nodes/"; - -export const TOP_LEVEL_BLOCK_PROP_KEYS = { - featureFlags: "Feature Flags", - global: "Global", -} as const; - -export const DISCOURSE_NODE_BLOCK_KEYS = { - template: "Template", - index: "Index", - specification: "Specification", -} as const; - -/* eslint-enable @typescript-eslint/naming-convention */ diff --git a/apps/roam/src/components/settings/data/defaultRelationsBlockProps.ts b/apps/roam/src/components/settings/data/defaultRelationsBlockProps.ts new file mode 100644 index 000000000..3f02ae700 --- /dev/null +++ b/apps/roam/src/components/settings/data/defaultRelationsBlockProps.ts @@ -0,0 +1,142 @@ +import type { DiscourseRelationSettings } from "~/components/settings/utils/zodSchema"; +/* eslint-disable @typescript-eslint/naming-convention */ // This is for nodePosition keys + +// TODO: Delete the original default relations in data/defaultRelations.ts when fully migrated. +const DEFAULT_RELATIONS_BLOCK_PROPS: DiscourseRelationSettings[] = [ + { + id: "informs", + label: "Informs", + source: "_EVD-node", + destination: "_QUE-node", + complement: "Informed By", + ifConditions: [ + { + triples: [ + ["Page", "is a", "source"], + ["Block", "references", "Page"], + ["Block", "is in page", "ParentPage"], + ["ParentPage", "is a", "destination"], + ], + nodePositions: { + "0": "100 57", + "1": "100 208", + "2": "100 345", + source: "281 57", + destination: "281 345", + }, + }, + ], + }, + { + id: "supports", + label: "Supports", + source: "_EVD-node", + destination: "_CLM-node", + complement: "Supported By", + ifConditions: [ + { + triples: [ + ["Page", "is a", "source"], + ["Block", "references", "Page"], + ["SBlock", "references", "SPage"], + ["SPage", "has title", "SupportedBy"], + ["SBlock", "has child", "Block"], + ["PBlock", "references", "ParentPage"], + ["PBlock", "has child", "SBlock"], + ["ParentPage", "is a", "destination"], + ], + nodePositions: { + "0": "250 325", + "1": "100 325", + "2": "100 200", + "3": "250 200", + "4": "400 200", + "5": "100 75", + "6": "250 75", + source: "400 325", + destination: "400 75", + }, + }, + { + triples: [ + ["Page", "is a", "destination"], + ["Block", "references", "Page"], + ["SBlock", "references", "SPage"], + ["SPage", "has title", "Supports"], + ["SBlock", "has child", "Block"], + ["PBlock", "references", "ParentPage"], + ["PBlock", "has child", "SBlock"], + ["ParentPage", "is a", "source"], + ], + nodePositions: { + "7": "250 325", + "8": "100 325", + "9": "100 200", + "10": "250 200", + "11": "400 200", + "12": "100 75", + "13": "250 75", + source: "400 75", + destination: "400 325", + }, + }, + ], + }, + { + id: "opposes", + label: "Opposes", + source: "_EVD-node", + destination: "_CLM-node", + complement: "Opposed By", + ifConditions: [ + { + triples: [ + ["Page", "is a", "source"], + ["Block", "references", "Page"], + ["SBlock", "references", "SPage"], + ["SPage", "has title", "OpposedBy"], + ["SBlock", "has child", "Block"], + ["PBlock", "references", "ParentPage"], + ["PBlock", "has child", "SBlock"], + ["ParentPage", "is a", "destination"], + ], + nodePositions: { + "0": "250 325", + "1": "100 325", + "2": "100 200", + "3": "250 200", + "4": "400 200", + "5": "100 75", + "6": "250 75", + source: "400 325", + destination: "400 75", + }, + }, + { + triples: [ + ["Page", "is a", "destination"], + ["Block", "references", "Page"], + ["SBlock", "references", "SPage"], + ["SPage", "has title", "Opposes"], + ["SBlock", "has child", "Block"], + ["PBlock", "references", "ParentPage"], + ["PBlock", "has child", "SBlock"], + ["ParentPage", "is a", "source"], + ], + nodePositions: { + "7": "250 325", + "8": "100 325", + "9": "100 200", + "10": "250 200", + "11": "400 200", + "12": "100 75", + "13": "250 75", + source: "400 75", + destination: "400 325", + }, + }, + ], + }, +]; + +export default DEFAULT_RELATIONS_BLOCK_PROPS; diff --git a/apps/roam/src/components/settings/utils/init.ts b/apps/roam/src/components/settings/utils/init.ts index d96f0445b..fad732604 100644 --- a/apps/roam/src/components/settings/utils/init.ts +++ b/apps/roam/src/components/settings/utils/init.ts @@ -2,36 +2,14 @@ import getPageUidByPageTitle from "roamjs-components/queries/getPageUidByPageTit import getShallowTreeByParentUid from "roamjs-components/queries/getShallowTreeByParentUid"; import { createPage, createBlock } from "roamjs-components/writes"; import setBlockProps from "~/utils/setBlockProps"; -import getBlockProps, { type json } from "~/utils/getBlockProps"; -// eslint-disable-next-line @typescript-eslint/naming-convention +import getBlockProps from "~/utils/getBlockProps"; import INITIAL_NODE_VALUES from "~/data/defaultDiscourseNodes"; import { DiscourseNodeSchema, - FeatureFlagsSchema, - GlobalSettingsSchema, - PersonalSettingsSchema, -} from "./zodSchema"; -import type { ZodSchema } from "zod"; -import { - DG_BLOCK_PROP_SETTINGS_PAGE_TITLE, - DISCOURSE_NODE_PAGE_PREFIX, - TOP_LEVEL_BLOCK_PROP_KEYS, - DISCOURSE_NODE_BLOCK_KEYS, -} from "../data/blockPropsSettingsConfig"; - -let cachedPersonalSettingsKey: string | null = null; - -const getPersonalSettingsKey = (): string => { - if (cachedPersonalSettingsKey !== null) { - return cachedPersonalSettingsKey; - } - cachedPersonalSettingsKey = window.roamAlphaAPI.user.uid() || ""; - return cachedPersonalSettingsKey; -}; - -const getDiscourseNodePageTitle = (nodeLabel: string): string => { - return `${DISCOURSE_NODE_PAGE_PREFIX}${nodeLabel}`; -}; + getTopLevelBlockPropsConfig, + getPersonalSettingsKey, +} from "~/components/settings/utils/zodSchema"; +import { DG_BLOCK_PROP_SETTINGS_PAGE_TITLE, DISCOURSE_NODE_PAGE_PREFIX } from "./zodSchema"; const ensurePageExists = async (pageTitle: string): Promise => { let pageUid = getPageUidByPageTitle(pageTitle); @@ -75,24 +53,6 @@ const ensureBlocksExist = async ( return existingBlockMap; }; -const ensurePersonalBlockExists = async ( - pageUid: string, - existingBlockMap: Record, -): Promise<{ key: string; uid: string }> => { - const personalKey = getPersonalSettingsKey(); - - if (existingBlockMap[personalKey]) { - return { key: personalKey, uid: existingBlockMap[personalKey] }; - } - - const uid = await createBlock({ - parentUid: pageUid, - node: { text: personalKey }, - }); - - return { key: personalKey, uid }; -}; - const buildBlockMap = (pageUid: string): Record => { const existingChildren = getShallowTreeByParentUid(pageUid); const blockMap: Record = {}; @@ -102,27 +62,19 @@ const buildBlockMap = (pageUid: string): Record => { return blockMap; }; -const initBlockPropsIfEmpty = (uid: string, schema: ZodSchema): void => { - const existingProps = getBlockProps(uid); - if (!existingProps || Object.keys(existingProps).length === 0) { - const defaults = schema.parse({}) as Record; - setBlockProps(uid, defaults, false); - } -}; - const initializeSettingsBlockProps = ( blockMap: Record, ): void => { - const configs = [ - { key: TOP_LEVEL_BLOCK_PROP_KEYS.featureFlags, schema: FeatureFlagsSchema }, - { key: TOP_LEVEL_BLOCK_PROP_KEYS.global, schema: GlobalSettingsSchema }, - { key: getPersonalSettingsKey(), schema: PersonalSettingsSchema }, - ]; + const configs = getTopLevelBlockPropsConfig(); for (const { key, schema } of configs) { const uid = blockMap[key]; if (uid) { - initBlockPropsIfEmpty(uid, schema); + const existingProps = getBlockProps(uid); + if (!existingProps || Object.keys(existingProps).length === 0) { + const defaults = schema.parse({}); + setBlockProps(uid, defaults, false); + } } } }; @@ -131,22 +83,36 @@ const initSettingsPageBlocks = async (): Promise> => { const pageUid = await ensurePageExists(DG_BLOCK_PROP_SETTINGS_PAGE_TITLE); const blockMap = buildBlockMap(pageUid); - const topLevelBlocks = Object.values(TOP_LEVEL_BLOCK_PROP_KEYS); + const topLevelBlocks = getTopLevelBlockPropsConfig().map(({ key }) => key); await ensureBlocksExist(pageUid, topLevelBlocks, blockMap); - const personalBlock = await ensurePersonalBlockExists(pageUid, blockMap); - blockMap[personalBlock.key] = personalBlock.uid; - initializeSettingsBlockProps(blockMap); return blockMap; }; -const ensureDiscourseNodePageExists = async ( - nodeLabel: string, -): Promise => { - const pageTitle = getDiscourseNodePageTitle(nodeLabel); - return ensurePageExists(pageTitle); +const hasNonDefaultNodes = (): boolean => { + const results = window.roamAlphaAPI.q(` + [:find ?uid ?title + :where + [?page :node/title ?title] + [?page :block/uid ?uid] + [(clojure.string/starts-with? ?title "${DISCOURSE_NODE_PAGE_PREFIX}")]] + `) as [string, string][]; + + for (const [pageUid] of results) { + const blockProps = getBlockProps(pageUid); + if (!blockProps) continue; + + const parsed = DiscourseNodeSchema.safeParse(blockProps); + if (!parsed.success) continue; + + if (parsed.data.backedBy !== "default") { + return true; + } + } + + return false; }; const initSingleDiscourseNode = async ( @@ -154,22 +120,10 @@ const initSingleDiscourseNode = async ( ): Promise<{ label: string; pageUid: string } | null> => { if (!node.text) return null; - const pageUid = await ensureDiscourseNodePageExists(node.text); + const pageUid = await ensurePageExists( + `${DISCOURSE_NODE_PAGE_PREFIX}${node.text}`, + ); const existingProps = getBlockProps(pageUid); - const blockMap = buildBlockMap(pageUid); - - for (const key of Object.values(DISCOURSE_NODE_BLOCK_KEYS)) { - if (!blockMap[key]) { - blockMap[key] = await createBlock({ - parentUid: pageUid, - node: { text: key }, - }); - } - } - - const templateUid = blockMap[DISCOURSE_NODE_BLOCK_KEYS.template]; - const indexUid = blockMap[DISCOURSE_NODE_BLOCK_KEYS.index]; - const specificationUid = blockMap[DISCOURSE_NODE_BLOCK_KEYS.specification]; if (!existingProps || Object.keys(existingProps).length === 0) { const nodeData = DiscourseNodeSchema.parse({ @@ -180,25 +134,20 @@ const initSingleDiscourseNode = async ( tag: node.tag || "", graphOverview: node.graphOverview ?? false, canvasSettings: node.canvasSettings || {}, - templateUid, - indexUid, - specificationUid, - backedBy: "user", + backedBy: "default", }); - setBlockProps(pageUid, nodeData as Record, false); - } else if ( - !existingProps.templateUid || - !existingProps.indexUid || - !existingProps.specificationUid - ) { - setBlockProps(pageUid, { templateUid, indexUid, specificationUid }, true); + setBlockProps(pageUid, nodeData, false); } return { label: node.text, pageUid }; }; const initDiscourseNodePages = async (): Promise> => { + if (hasNonDefaultNodes()) { + return {}; + } + const results = await Promise.all( INITIAL_NODE_VALUES.map((node) => initSingleDiscourseNode(node)), ); @@ -213,9 +162,13 @@ const initDiscourseNodePages = async (): Promise> => { return nodePageUids; }; -const printAllSettings = (blockMap: Record): void => { - const featureFlagsUid = blockMap[TOP_LEVEL_BLOCK_PROP_KEYS.featureFlags]; - const globalUid = blockMap[TOP_LEVEL_BLOCK_PROP_KEYS.global]; +const printAllSettings = ( + blockMap: Record, + nodePageUids: Record, +): void => { + const configs = getTopLevelBlockPropsConfig(); + const featureFlagsUid = blockMap[configs.find(({ key }) => key === "Feature Flags")?.key ?? ""]; + const globalUid = blockMap[configs.find(({ key }) => key === "Global")?.key ?? ""]; const personalKey = getPersonalSettingsKey(); const personalUid = blockMap[personalKey]; @@ -223,100 +176,33 @@ const printAllSettings = (blockMap: Record): void => { const globalSettings = globalUid ? getBlockProps(globalUid) : null; const personalSettings = personalUid ? getBlockProps(personalUid) : null; - console.group("🔧 Discourse Graph Settings Initialized"); - - if (featureFlags) { - console.group("🚩 Feature Flags"); - console.table(featureFlags); - console.groupEnd(); - } - - if (globalSettings) { - console.group("🌍 Global Settings"); - console.log("Trigger:", globalSettings?.Trigger || "(empty)"); - console.log( - "Canvas Page Format:", - globalSettings?.["Canvas Page Format"] || "(empty)", - ); + console.group("🔧 Discourse Graph Settings Initialized (RAW DATA)"); - if (globalSettings?.["Left Sidebar"]) { - console.group("📂 Left Sidebar"); - console.log(globalSettings["Left Sidebar"]); - console.groupEnd(); - } + console.group(`🚩 Feature Flags (uid: ${featureFlagsUid})`); + console.log("Raw block props:", JSON.stringify(featureFlags, null, 2)); + console.groupEnd(); - if (globalSettings?.Export) { - console.group("📤 Export Settings"); - console.table(globalSettings.Export); - console.groupEnd(); - } + console.group(`🌍 Global Settings (uid: ${globalUid})`); + console.log("Raw block props:", JSON.stringify(globalSettings, null, 2)); + console.groupEnd(); - if (globalSettings?.["Suggestive Mode"]) { - console.group("💡 Suggestive Mode"); - console.log(globalSettings["Suggestive Mode"]); - console.groupEnd(); - } + console.group(`👤 Personal Settings (uid: ${personalUid})`); + console.log("Raw block props:", JSON.stringify(personalSettings, null, 2)); + console.groupEnd(); + console.group("📝 Discourse Nodes"); + for (const [nodeLabel, pageUid] of Object.entries(nodePageUids)) { + const nodeProps = getBlockProps(pageUid); + console.group(`${nodeLabel} (uid: ${pageUid})`); + console.log("Raw block props:", JSON.stringify(nodeProps, null, 2)); console.groupEnd(); } + console.groupEnd(); - if (personalSettings) { - console.group("👤 Personal Settings"); - console.log( - "Personal Node Menu Trigger:", - personalSettings?.["Personal Node Menu Trigger"] || "(empty)", - ); - console.log( - "Node Search Menu Trigger:", - personalSettings?.["Node Search Menu Trigger"] || "(empty)", - ); - console.log( - "Discourse Tool Shortcut:", - personalSettings?.["Discourse Tool Shortcut"] || "(empty)", - ); - - console.group("🎛️ Toggles"); - const toggles = { - // eslint-disable-next-line @typescript-eslint/naming-convention - "Discourse Context Overlay": - personalSettings?.["Discourse Context Overlay"], - // eslint-disable-next-line @typescript-eslint/naming-convention - "Suggestive Mode Overlay": personalSettings?.["Suggestive Mode Overlay"], - // eslint-disable-next-line @typescript-eslint/naming-convention - "Overlay in Canvas": personalSettings?.["Overlay in Canvas"], - // eslint-disable-next-line @typescript-eslint/naming-convention - "Text Selection Popup": personalSettings?.["Text Selection Popup"], - // eslint-disable-next-line @typescript-eslint/naming-convention - "Disable Sidebar Open": personalSettings?.["Disable Sidebar Open"], - // eslint-disable-next-line @typescript-eslint/naming-convention - "Page Preview": personalSettings?.["Page Preview"], - // eslint-disable-next-line @typescript-eslint/naming-convention - "Hide Feedback Button": personalSettings?.["Hide Feedback Button"], - // eslint-disable-next-line @typescript-eslint/naming-convention - "Streamline Styling": personalSettings?.["Streamline Styling"], - // eslint-disable-next-line @typescript-eslint/naming-convention - "Auto Canvas Relations": personalSettings?.["Auto Canvas Relations"], - // eslint-disable-next-line @typescript-eslint/naming-convention - "Disable Product Diagnostics": - personalSettings?.["Disable Product Diagnostics"], - }; - console.table(toggles); - console.groupEnd(); - - if (personalSettings?.["Left Sidebar"]) { - console.group("📂 Personal Left Sidebar"); - console.log(personalSettings["Left Sidebar"]); - console.groupEnd(); - } - - if (personalSettings?.Query) { - console.group("🔍 Query Settings"); - console.table(personalSettings.Query); - console.groupEnd(); - } - - console.groupEnd(); - } + const relations = (globalSettings as Record)?.Relations; + console.group("🔗 Discourse Relations"); + console.log("Relations:", JSON.stringify(relations, null, 2)); + console.groupEnd(); console.groupEnd(); }; @@ -330,7 +216,9 @@ export const initSchema = async (): Promise => { const blockUids = await initSettingsPageBlocks(); const nodePageUids = await initDiscourseNodePages(); - printAllSettings(blockUids); + setTimeout(() => { + printAllSettings(blockUids, nodePageUids); + }, 2000); return { blockUids, nodePageUids }; }; diff --git a/apps/roam/src/components/settings/utils/zodSchema.example.ts b/apps/roam/src/components/settings/utils/zodSchema.example.ts index fe6cbff69..5a5890367 100644 --- a/apps/roam/src/components/settings/utils/zodSchema.example.ts +++ b/apps/roam/src/components/settings/utils/zodSchema.example.ts @@ -71,13 +71,17 @@ const discourseNodeSettings: DiscourseNodeSettings = { Confidence: "confidence-attr-uid", }, overlay: "Status", - index: [ - { - type: "filter", - condition: "has attribute", - attribute: "Status", - }, - ], + index: { + conditions: [ + { + type: "clause", + source: "Claim", + relation: "has attribute", + target: "Status", + }, + ], + selections: [], + }, suggestiveRules: { template: [], embeddingRef: "((embed-ref))", diff --git a/apps/roam/src/components/settings/utils/zodSchema.ts b/apps/roam/src/components/settings/utils/zodSchema.ts index fc7a2d06d..e82093a19 100644 --- a/apps/roam/src/components/settings/utils/zodSchema.ts +++ b/apps/roam/src/components/settings/utils/zodSchema.ts @@ -1,4 +1,5 @@ import { z } from "zod"; +import DEFAULT_RELATIONS_BLOCK_PROPS from "~/components/settings/data/defaultRelationsBlockProps"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -61,6 +62,11 @@ export const SelectionSchema = z.object({ label: z.string(), }); +export const IndexSchema = z.object({ + conditions: z.array(ConditionSchema).default([]), + selections: z.array(SelectionSchema).default([]), +}); + type RoamNode = { text: string; children?: RoamNode[]; @@ -120,7 +126,7 @@ export const DiscourseNodeSchema = z.object({ .optional() .transform((val) => val ?? {}), overlay: stringWithDefault(""), - index: z.unknown().nullable().optional(), + index: IndexSchema.nullable().optional(), suggestiveRules: SuggestiveRulesSchema.nullable().optional(), embeddingRef: stringWithDefault(""), isFirstChild: z @@ -190,12 +196,12 @@ export const LeftSidebarGlobalSettingsSchema = z.object({ }); export const GlobalSettingsSchema = z.object({ - Trigger: z.string().default(""), - "Canvas Page Format": z.string().default(""), + Trigger: z.string().default("\\"), + "Canvas Page Format": z.string().default("Canvas/*"), "Left Sidebar": LeftSidebarGlobalSettingsSchema.default({}), Export: ExportSettingsSchema.default({}), "Suggestive Mode": SuggestiveModeGlobalSettingsSchema.default({}), - Relations: z.array(DiscourseRelationSchema).default([]), + Relations: z.array(DiscourseRelationSchema).default(DEFAULT_RELATIONS_BLOCK_PROPS), }); export const PersonalSectionSchema = z.object({ @@ -234,7 +240,7 @@ export const QuerySettingsSchema = z.object({ export const PersonalSettingsSchema = z.object({ "Left Sidebar": LeftSidebarPersonalSettingsSchema, "Personal Node Menu Trigger": z.string().default(""), - "Node Search Menu Trigger": z.string().default(""), + "Node Search Menu Trigger": z.string().default("@"), "Discourse Tool Shortcut": z.string().default(""), "Discourse Context Overlay": z.boolean().default(false), "Suggestive Mode Overlay": z.boolean().default(false), @@ -254,7 +260,22 @@ export const GithubSettingsSchema = z.object({ "selected-repo": z.string().optional(), }); -/* eslint-enable @typescript-eslint/naming-convention */ +let cachedPersonalSettingsKey: string | null = null; +export const getPersonalSettingsKey = (): string => { + if (cachedPersonalSettingsKey !== null) return cachedPersonalSettingsKey; + cachedPersonalSettingsKey = window.roamAlphaAPI.user.uid() || ""; + return cachedPersonalSettingsKey; +}; + +export const getTopLevelBlockPropsConfig = () => [ + { key: "Feature Flags", schema: FeatureFlagsSchema }, + { key: "Global", schema: GlobalSettingsSchema }, + { key: getPersonalSettingsKey(), schema: PersonalSettingsSchema }, +]; + +export const DG_BLOCK_PROP_SETTINGS_PAGE_TITLE = + "roam/js/discourse-graph"; +export const DISCOURSE_NODE_PAGE_PREFIX = "discourse-graph/nodes/"; export type CanvasSettings = z.infer; export type SuggestiveRules = z.infer; @@ -283,3 +304,4 @@ export type GithubSettings = z.infer; export type QueryCondition = z.infer; export type QuerySelection = z.infer; export type RoamNodeType = z.infer; +export type Index = z.infer; diff --git a/apps/roam/src/index.ts b/apps/roam/src/index.ts index 5b877173c..b44469c95 100644 --- a/apps/roam/src/index.ts +++ b/apps/roam/src/index.ts @@ -41,6 +41,7 @@ import { STREAMLINE_STYLING_KEY, DISALLOW_DIAGNOSTICS, } from "./data/userSettings"; +import { initSchema } from "./components/settings/utils/init"; export const DEFAULT_CANVAS_PAGE_FORMAT = "Canvas/*"; @@ -78,6 +79,8 @@ export default runExtension(async (onloadArgs) => { await initializeDiscourseNodes(); refreshConfigTree(); + // For testing purposes + await initSchema(); addGraphViewNodeStyling(); registerCommandPaletteCommands(onloadArgs); createSettingsPanel(onloadArgs);