Skip to content

Commit 94fbfd1

Browse files
authored
ENG-477 Create new discourse node creation command (#884)
* feat(roam): add command palette discourse node creation * refactor(roam): enhance discourse node creation command with selection handling - Introduced getSelectionStartForBlock function to accurately retrieve the cursor position in the focused block. - Updated createDiscourseNodeFromCommand to handle cases where no block is focused, providing user feedback. - Renamed command to "DG: Create/Insert Discourse node" for clarity. * . * .
1 parent e2b049e commit 94fbfd1

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

apps/roam/src/utils/registerCommandPaletteCommands.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import getDiscourseNodes from "./getDiscourseNodes";
1212
import fireQuery from "./fireQuery";
1313
import { excludeDefaultNodes } from "~/utils/getDiscourseNodes";
1414
import { render as renderSettings } from "~/components/settings/Settings";
15+
import { renderModifyNodeDialog } from "~/components/ModifyNodeDialog";
16+
import getTextByBlockUid from "roamjs-components/queries/getTextByBlockUid";
17+
import getUids from "roamjs-components/dom/getUids";
1518
import {
1619
getOverlayHandler,
1720
onPageRefObserverChange,
@@ -155,6 +158,74 @@ export const registerCommandPaletteCommands = (onloadArgs: OnloadArgs) => {
155158
renderSettings({ onloadArgs });
156159
};
157160

161+
const getSelectionStartForBlock = (uid: string): number => {
162+
const activeElement = document.activeElement;
163+
const isFocusedTextarea =
164+
activeElement instanceof HTMLTextAreaElement &&
165+
activeElement.classList.contains("rm-block-input") &&
166+
getUids(activeElement).blockUid === uid;
167+
if (isFocusedTextarea) return activeElement.selectionStart;
168+
const textareas = document.querySelectorAll("textarea.rm-block-input");
169+
for (const el of textareas) {
170+
const textarea = el as HTMLTextAreaElement;
171+
if (getUids(textarea).blockUid === uid) return textarea.selectionStart;
172+
}
173+
return (getTextByBlockUid(uid) || "").length;
174+
};
175+
176+
const createDiscourseNodeFromCommand = () => {
177+
posthog.capture("Discourse Node: Create Command Triggered");
178+
const focusedBlock = window.roamAlphaAPI.ui.getFocusedBlock();
179+
const uid = focusedBlock?.["block-uid"];
180+
const windowId = focusedBlock?.["window-id"] || "main-window";
181+
182+
const selectionStart = uid ? getSelectionStartForBlock(uid) : 0;
183+
184+
const defaultNodeType =
185+
getDiscourseNodes().filter(excludeDefaultNodes)[0]?.type;
186+
if (!defaultNodeType) {
187+
renderToast({
188+
id: "create-discourse-node-command-no-types",
189+
content: "No discourse node types found in settings.",
190+
});
191+
return;
192+
}
193+
194+
renderModifyNodeDialog({
195+
mode: "create",
196+
nodeType: defaultNodeType,
197+
initialValue: { text: "", uid: "" },
198+
extensionAPI,
199+
onSuccess: async (result) => {
200+
if (!uid) {
201+
renderToast({
202+
id: "create-discourse-node-command-no-block",
203+
content: "No block focused to insert a discourse node.",
204+
});
205+
return;
206+
}
207+
const originalText = getTextByBlockUid(uid) || "";
208+
const pageRef = `[[${result.text}]]`;
209+
const newText = `${originalText.substring(0, selectionStart)}${pageRef}${originalText.substring(selectionStart)}`;
210+
const newCursorPosition = selectionStart + pageRef.length;
211+
212+
await updateBlock({ uid, text: newText });
213+
214+
await window.roamAlphaAPI.ui.setBlockFocusAndSelection({
215+
location: {
216+
// eslint-disable-next-line @typescript-eslint/naming-convention
217+
"block-uid": uid,
218+
// eslint-disable-next-line @typescript-eslint/naming-convention
219+
"window-id": windowId,
220+
},
221+
selection: { start: newCursorPosition },
222+
});
223+
return;
224+
},
225+
onClose: () => {},
226+
});
227+
};
228+
158229
const toggleDiscourseContextOverlay = async () => {
159230
const currentValue =
160231
(extensionAPI.settings.get("discourse-context-overlay") as boolean) ??
@@ -212,6 +283,10 @@ export const registerCommandPaletteCommands = (onloadArgs: OnloadArgs) => {
212283
};
213284

214285
// Roam organizes commands alphabetically
286+
void addCommand(
287+
"DG: Create/Insert discourse node",
288+
createDiscourseNodeFromCommand,
289+
);
215290
void addCommand("DG: Export - Current page", exportCurrentPage);
216291
void addCommand("DG: Export - Discourse graph", exportDiscourseGraph);
217292
void addCommand("DG: Open - Discourse settings", renderSettingsPopup);

0 commit comments

Comments
 (0)