diff --git a/apps/roam/src/components/CreateNodeDialog.tsx b/apps/roam/src/components/CreateNodeDialog.tsx index 4e78e6c13..17853c63b 100644 --- a/apps/roam/src/components/CreateNodeDialog.tsx +++ b/apps/roam/src/components/CreateNodeDialog.tsx @@ -11,6 +11,7 @@ import getDiscourseNodes, { } from "~/utils/getDiscourseNodes"; import { getNewDiscourseNodeText } from "~/utils/formatUtils"; import MenuItemSelect from "roamjs-components/components/MenuItemSelect"; +import createBlock from "roamjs-components/writes/createBlock"; export type CreateNodeDialogProps = { onClose: () => void; @@ -48,6 +49,20 @@ const CreateNodeDialog = ({ if (!title.trim()) return; setLoading(true); + const query = `[:find ?parentUid ?order + :keys parentUid order + :in $ ?block-uid + :where + [?e :block/uid ?block-uid] + [?e :block/order ?order] + [?p :block/children ?e] + [?p :block/uid ?parentUid] + ]`; + + const blockData = ( + await window.roamAlphaAPI.data.async.q(query, sourceBlockUid) + )?.[0] as unknown as { parentUid: string; order: number }; + const formattedTitle = await getNewDiscourseNodeText({ text: title.trim(), nodeType: selectedType.type, @@ -71,7 +86,16 @@ const CreateNodeDialog = ({ // the correct reference. The reference format should be determined by the // node's specification. const pageRef = `[[${formattedTitle}]]`; - await updateBlock({ uid: sourceBlockUid, text: pageRef }); + const newBlockUid = await createBlock({ + node: { text: pageRef }, + parentUid: blockData.parentUid, + order: blockData.order, + }); + + await window.roamAlphaAPI.moveBlock({ + block: { uid: sourceBlockUid }, + location: { "parent-uid": newBlockUid, order: 0 }, + }); const newCursorPosition = pageRef.length; const windowId = diff --git a/apps/roam/src/components/DiscourseNodeMenu.tsx b/apps/roam/src/components/DiscourseNodeMenu.tsx index 62160488c..24fd56602 100644 --- a/apps/roam/src/components/DiscourseNodeMenu.tsx +++ b/apps/roam/src/components/DiscourseNodeMenu.tsx @@ -113,7 +113,7 @@ const NodeMenu = ({ setTimeout(() => { const currentText = textarea.value; const cursorPos = textarea.selectionStart; - const textToInsert = `#${tag} `; + const textToInsert = `#${tag.replace(/^#/, "")} `; const newText = `${currentText.substring( 0, @@ -249,9 +249,13 @@ const NodeMenu = ({ setActiveIndex(i)} diff --git a/apps/roam/src/components/settings/NodeConfig.tsx b/apps/roam/src/components/settings/NodeConfig.tsx index f31be1a5b..1d0af9a80 100644 --- a/apps/roam/src/components/settings/NodeConfig.tsx +++ b/apps/roam/src/components/settings/NodeConfig.tsx @@ -285,7 +285,7 @@ const NodeConfig = ({ onChange={handleTagChange} onBlur={handleTagBlur} error={tagError} - placeholder={`${node.text}`} + placeholder={`#${node.text.toLowerCase()}`} /> } diff --git a/apps/roam/src/utils/initializeObserversAndListeners.ts b/apps/roam/src/utils/initializeObserversAndListeners.ts index bce287b0a..5c438e2bd 100644 --- a/apps/roam/src/utils/initializeObserversAndListeners.ts +++ b/apps/roam/src/utils/initializeObserversAndListeners.ts @@ -44,6 +44,7 @@ import { findBlockElementFromSelection, } from "~/utils/renderTextSelectionPopup"; import { renderNodeTagPopupButton } from "./renderNodeTagPopup"; +import { formatHexColor } from "~/components/settings/DiscourseNodeCanvasSettings"; const debounce = (fn: () => void, delay = 250) => { let timeout: number; @@ -102,7 +103,18 @@ export const initObservers = async ({ className: "rm-page-ref--tag", tag: "SPAN", callback: (s: HTMLSpanElement) => { - renderNodeTagPopupButton(s, onloadArgs.extensionAPI); + const tag = s.getAttribute("data-tag"); + if (tag) { + for (const node of getDiscourseNodes()) { + if (tag.toLowerCase() === node.tag?.toLowerCase()) { + renderNodeTagPopupButton(s, node, onloadArgs.extensionAPI); + if (node.canvasSettings?.color) { + s.style.color = formatHexColor(node.canvasSettings.color); + } + break; + } + } + } }, }); diff --git a/apps/roam/src/utils/renderNodeTagPopup.tsx b/apps/roam/src/utils/renderNodeTagPopup.tsx index 460d2a08a..d8f2cdfd7 100644 --- a/apps/roam/src/utils/renderNodeTagPopup.tsx +++ b/apps/roam/src/utils/renderNodeTagPopup.tsx @@ -5,14 +5,16 @@ import { renderCreateNodeDialog } from "~/components/CreateNodeDialog"; import { OnloadArgs } from "roamjs-components/types"; import getUids from "roamjs-components/dom/getUids"; import getTextByBlockUid from "roamjs-components/queries/getTextByBlockUid"; -import getDiscourseNodes from "./getDiscourseNodes"; +import { type DiscourseNode } from "./getDiscourseNodes"; export const renderNodeTagPopupButton = ( parent: HTMLSpanElement, + matchedNode: DiscourseNode, extensionAPI: OnloadArgs["extensionAPI"], ) => { if (parent.dataset.attributeButtonRendered === "true") return; + const rect = parent.getBoundingClientRect(); parent.dataset.attributeButtonRendered = "true"; const wrapper = document.createElement("span"); wrapper.style.position = "relative"; @@ -24,26 +26,14 @@ export const renderNodeTagPopupButton = ( reactRoot.style.position = "absolute"; reactRoot.style.top = "0"; reactRoot.style.left = "0"; - reactRoot.style.width = "100%"; - reactRoot.style.height = "100%"; + reactRoot.style.width = `${rect.width}px`; + reactRoot.style.height = `${rect.height}px`; reactRoot.style.pointerEvents = "none"; reactRoot.style.zIndex = "10"; wrapper.appendChild(reactRoot); const textContent = parent.textContent?.trim() || ""; - const tagAttr = parent.getAttribute("data-tag") || textContent; - const tag = tagAttr.replace(/^#/, "").toLowerCase(); - const discourseNodes = getDiscourseNodes(); - const discourseTagSet = new Set( - discourseNodes.map((n) => n.tag?.toLowerCase()).filter(Boolean), - ); - if (!discourseTagSet.has(tag)) return; - - const matchedNode = discourseNodes.find((n) => n.tag?.toLowerCase() === tag); - - if (!matchedNode) return; - const blockInputElement = parent.closest(".rm-block__input"); const blockUid = blockInputElement ? getUids(blockInputElement as HTMLDivElement).blockUid @@ -74,8 +64,11 @@ export const renderNodeTagPopupButton = ( @@ -84,7 +77,7 @@ export const renderNodeTagPopupButton = ( position={Position.TOP} modifiers={{ offset: { - offset: "0, 10", + offset: `${rect.width / 2}px, 10`, }, arrow: { enabled: false,