diff --git a/apps/roam/src/components/settings/DiscourseNodeSuggestiveRules.tsx b/apps/roam/src/components/settings/DiscourseNodeSuggestiveRules.tsx index 70088313f..9a5fac825 100644 --- a/apps/roam/src/components/settings/DiscourseNodeSuggestiveRules.tsx +++ b/apps/roam/src/components/settings/DiscourseNodeSuggestiveRules.tsx @@ -1,6 +1,6 @@ import React, { useState, useMemo, useEffect, useRef } from "react"; import { Button, Intent } from "@blueprintjs/core"; -import BlocksPanel from "roamjs-components/components/ConfigPanels/BlocksPanel"; +import DualWriteBlocksPanel from "./components/EphemeralBlocksPanel"; import getSubTree from "roamjs-components/util/getSubTree"; import { DiscourseNode } from "~/utils/getDiscourseNodes"; import extractRef from "roamjs-components/util/extractRef"; @@ -12,6 +12,8 @@ import { DiscourseNodeTextPanel, } from "./components/BlockPropSettingPanels"; +const TEMPLATE_SETTING_KEYS = ["template"]; + const BlockRenderer = ({ uid }: { uid: string }) => { const containerRef = useRef(null); @@ -81,13 +83,12 @@ const DiscourseNodeSuggestiveRules = ({ return (
- { return tag.replace(/^#+/, "").trim().toUpperCase(); }; @@ -342,13 +344,12 @@ const NodeConfig = ({ title="Template" panel={
-
} diff --git a/apps/roam/src/components/settings/components/BlockPropSettingPanels.tsx b/apps/roam/src/components/settings/components/BlockPropSettingPanels.tsx index 1e1b0d6f2..0002c8028 100644 --- a/apps/roam/src/components/settings/components/BlockPropSettingPanels.tsx +++ b/apps/roam/src/components/settings/components/BlockPropSettingPanels.tsx @@ -582,7 +582,7 @@ const createDiscourseNodeSetter = (keys: string[], value: json): void => setDiscourseNodeSetting(nodeType, keys, value); -type DiscourseNodeBaseProps = { +export type DiscourseNodeBaseProps = { nodeType: string; title: string; description: string; diff --git a/apps/roam/src/components/settings/components/EphemeralBlocksPanel.tsx b/apps/roam/src/components/settings/components/EphemeralBlocksPanel.tsx new file mode 100644 index 000000000..92dead099 --- /dev/null +++ b/apps/roam/src/components/settings/components/EphemeralBlocksPanel.tsx @@ -0,0 +1,109 @@ +import React, { useRef, useEffect, useCallback } from "react"; +import { Label } from "@blueprintjs/core"; +import Description from "roamjs-components/components/Description"; +import createBlock from "roamjs-components/writes/createBlock"; +import getFullTreeByParentUid from "roamjs-components/queries/getFullTreeByParentUid"; +import getFirstChildUidByBlockUid from "roamjs-components/queries/getFirstChildUidByBlockUid"; +import type { TreeNode } from "roamjs-components/types"; +import type { RoamNodeType } from "~/components/settings/utils/zodSchema"; +import { setDiscourseNodeSetting } from "~/components/settings/utils/accessors"; +import type { DiscourseNodeBaseProps } from "./BlockPropSettingPanels"; + +const DEBOUNCE_MS = 250; + +type DualWriteBlocksPanelProps = DiscourseNodeBaseProps & { + uid: string; +}; + +const serializeBlockTree = (children: TreeNode[]): RoamNodeType[] => + children + .sort((a, b) => a.order - b.order) + .map((child) => ({ + text: child.text, + ...(child.heading && { heading: child.heading as 0 | 1 | 2 | 3 }), + ...(child.open === false && { open: false }), + ...(child.children.length > 0 && { + children: serializeBlockTree(child.children), + }), + })); + +const DualWriteBlocksPanel = ({ + nodeType, + settingKeys, + title, + description, + uid, +}: DualWriteBlocksPanelProps) => { + const containerRef = useRef(null); + const debounceRef = useRef(0); + const pullWatchArgsRef = useRef< + [string, string, (before: unknown, after: unknown) => void] | null + >(null); + + const handleChange = useCallback(() => { + window.clearTimeout(debounceRef.current); + debounceRef.current = window.setTimeout(() => { + const tree = getFullTreeByParentUid(uid); + const serialized = serializeBlockTree(tree.children); + setDiscourseNodeSetting(nodeType, settingKeys, serialized); + }, DEBOUNCE_MS); + }, [uid, nodeType, settingKeys]); + + useEffect(() => { + const el = containerRef.current; + if (!el || !uid) return; + + const pattern = "[:block/string :block/order {:block/children ...}]"; + const entityId = `[:block/uid "${uid}"]`; + const callback = () => handleChange(); + + const registerPullWatch = () => { + pullWatchArgsRef.current = [pattern, entityId, callback]; + window.roamAlphaAPI.data.addPullWatch(pattern, entityId, callback); + }; + + if (!getFirstChildUidByBlockUid(uid)) { + void createBlock({ node: { text: " " }, parentUid: uid }).then(() => { + el.innerHTML = ""; + void window.roamAlphaAPI.ui.components.renderBlock({ uid, el }); + registerPullWatch(); + }); + } else { + el.innerHTML = ""; + void window.roamAlphaAPI.ui.components.renderBlock({ uid, el }); + registerPullWatch(); + } + + return () => { + window.clearTimeout(debounceRef.current); + if (pullWatchArgsRef.current) { + window.roamAlphaAPI.data.removePullWatch(...pullWatchArgsRef.current); + pullWatchArgsRef.current = null; + } + }; + }, [uid, handleChange]); + + return ( + <> + + +
+ + ); +}; + +export default DualWriteBlocksPanel; diff --git a/apps/roam/src/components/settings/utils/zodSchema.example.ts b/apps/roam/src/components/settings/utils/zodSchema.example.ts index ee9187912..544055b4f 100644 --- a/apps/roam/src/components/settings/utils/zodSchema.example.ts +++ b/apps/roam/src/components/settings/utils/zodSchema.example.ts @@ -30,10 +30,6 @@ const canvasSettings: CanvasSettings = { }; const suggestiveRules: SuggestiveRules = { - template: [ - { text: "Summary::", heading: 2 }, - { text: "Key Points::", heading: 2, children: [{ text: "" }] }, - ], embeddingRef: "((block-uid-123))", isFirstChild: true, }; diff --git a/apps/roam/src/components/settings/utils/zodSchema.ts b/apps/roam/src/components/settings/utils/zodSchema.ts index 3c354f8df..76be0a5d7 100644 --- a/apps/roam/src/components/settings/utils/zodSchema.ts +++ b/apps/roam/src/components/settings/utils/zodSchema.ts @@ -75,7 +75,6 @@ export const RoamNodeSchema: z.ZodType = z.lazy(() => ); export const SuggestiveRulesSchema = z.object({ - template: z.array(RoamNodeSchema).default([]), embeddingRef: z.string().default(""), isFirstChild: z.boolean().default(false), });