From dca60bba294857c76c77ca6bc9bfa6b383a8e45f Mon Sep 17 00:00:00 2001 From: Michael Gartner Date: Mon, 17 Aug 2026 15:01:43 -0600 Subject: [PATCH] Resolve raw Roam UIDs and block references directly --- CHANGELOG.md | 4 +++ src/utils/quickSwitcher.ts | 11 ++++++- src/utils/quickSwitcherEntries.ts | 19 +++++++++-- tests/quickSwitcher.test.ts | 54 +++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 825c32f..4841d1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1 +1,5 @@ # Changelog + +## Unreleased + +- Allow Manage to resolve raw Roam UIDs and `((block reference))` inputs directly. diff --git a/src/utils/quickSwitcher.ts b/src/utils/quickSwitcher.ts index 00b37d5..7cbf6e5 100644 --- a/src/utils/quickSwitcher.ts +++ b/src/utils/quickSwitcher.ts @@ -3,11 +3,12 @@ import type { QuickSwitcherCommandPaletteSettings, QuickSwitcherTargetType, } from "~/types/quickSwitcher"; +import { BLOCK_REF_REGEX } from "roamjs-components/dom/constants"; const isRecord = (value: unknown): value is Record => typeof value === "object" && value !== null; -const BLOCK_REF_REGEX = /\(\(([A-Za-z0-9_-]+)\)\)/; +const ROAM_UID_REGEX = /^[\w-]{9,10}$/; export const DEFAULT_COMMAND_PALETTE_PREFIX = "QS: "; const LEGACY_COMMAND_PALETTE_PREFIX = "Q S - "; @@ -315,5 +316,13 @@ export const extractBlockRefUid = ({ return match?.[1] || null; }; +export const parseRoamUid = ({ value }: { value: string }): string | null => { + const normalizedValue = value.trim(); + if (ROAM_UID_REGEX.test(normalizedValue)) { + return normalizedValue; + } + return extractBlockRefUid({ value: normalizedValue }); +}; + export const createBookmarkId = (): string => `${Date.now()}-${Math.random().toString(36).slice(2, 10)}`; diff --git a/src/utils/quickSwitcherEntries.ts b/src/utils/quickSwitcherEntries.ts index 9281d8d..c68ff56 100644 --- a/src/utils/quickSwitcherEntries.ts +++ b/src/utils/quickSwitcherEntries.ts @@ -6,9 +6,9 @@ import { buildRoamPageUrl, createBookmarkId, deriveBlockTitle, - extractBlockRefUid, getBookmarkTargetType, getBookmarkTargetUid, + parseRoamUid, parsePageUidFromUrl, } from "~/utils/quickSwitcher"; @@ -87,10 +87,10 @@ const isPageUrlInput = ({ entry }: { entry: string }): boolean => const getUidFromEntryInput = ({ value }: { value: string }): string => { const normalizedValue = value.trim(); return ( - extractBlockRefUid({ value: normalizedValue }) || + parseRoamUid({ value: normalizedValue }) || (isPageUrlInput({ entry: normalizedValue }) ? parsePageUidFromUrl({ url: normalizedValue }) - : normalizedValue) || + : null) || "" ); }; @@ -245,6 +245,19 @@ export const searchEntries = async ({ savedTargetKeys: Set; searchApi?: RoamSearchApi; }): Promise => { + const directUid = getUidFromEntryInput({ value: query }); + if (directUid) { + const directSuggestion = await resolveUidToSuggestion({ uid: directUid }); + if ( + directSuggestion && + !savedTargetKeys.has( + getSuggestionTargetKey({ suggestion: directSuggestion }), + ) + ) { + return [directSuggestion]; + } + } + const options: RoamSearchOptions = { "hide-code-blocks": false, limit: MAX_SEARCH_RESULTS, diff --git a/tests/quickSwitcher.test.ts b/tests/quickSwitcher.test.ts index 54da636..2009be6 100644 --- a/tests/quickSwitcher.test.ts +++ b/tests/quickSwitcher.test.ts @@ -8,6 +8,7 @@ import { getCommandPaletteCommandLabel, normalizeCommandPaletteSettings, parsePageUidFromUrl, + parseRoamUid, parseStoredBookmarks, parseStoredCommandPaletteSettings, toAbsoluteUrl, @@ -322,6 +323,59 @@ test("extracts block uids from roam block refs", () => { expect(extractBlockRefUid({ value: "not a block ref" })).toBeNull(); }); +test("resolves raw and block reference uid searches directly", async () => { + const originalWindow = globalThis.window; + Object.defineProperty(globalThis, "window", { + configurable: true, + value: { + location: { + href: "https://roamresearch.com/#/app/test-graph/daily-notes", + origin: "https://roamresearch.com", + }, + roamAlphaAPI: { + data: { + backend: { + q: async (query: string): Promise<[string][]> => + query.includes(":node/title") ? [["Direct UID page"]] : [], + }, + }, + }, + }, + writable: true, + }); + + try { + const searchApi = async (): Promise => { + throw new Error("Text search should not run for a resolvable UID"); + }; + + for (const query of ["acW-i9uMD", "((acW-i9uMD))"]) { + await expect( + searchEntries({ query, savedTargetKeys: new Set(), searchApi }), + ).resolves.toEqual([ + expect.objectContaining({ + targetType: "page", + title: "Direct UID page", + uid: "acW-i9uMD", + }), + ]); + } + } finally { + Object.defineProperty(globalThis, "window", { + configurable: true, + value: originalWindow, + writable: true, + }); + } +}); + +test("parses raw and block reference Roam uids", () => { + expect(parseRoamUid({ value: "acW-i9uMD" })).toBe("acW-i9uMD"); + expect(parseRoamUid({ value: "((acW-i9uMD))" })).toBe("acW-i9uMD"); + expect(parseRoamUid({ value: "a page title" })).toBeNull(); + expect(parseRoamUid({ value: "short" })).toBeNull(); +}); + test("resolves relative urls to absolute urls", () => { expect(toAbsoluteUrl({ url: "/#/app/graph/page/abc" })).toContain( "/#/app/graph/page/abc",