Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions apps/roam/src/components/DiscourseContextOverlay.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ type DiscourseContextOverlayBaseProps = {
type DiscourseContextOverlayProps = DiscourseContextOverlayBaseProps &
({ tag: string; uid?: never } | { tag?: never; uid: string });

const ICON_SIZE = 16;

const DiscourseContextOverlay = ({
tag,
id,
Expand Down Expand Up @@ -169,6 +171,7 @@ const DiscourseContextOverlay = ({
icon={"diagram-tree"}
color={iconColor}
style={{ opacity: `${Number(opacity) / 100}` }}
size={ICON_SIZE}
/>
<span
className={`mr-1 leading-none opacity-${opacity}`}
Expand All @@ -180,6 +183,7 @@ const DiscourseContextOverlay = ({
icon={"link"}
color={iconColor}
style={{ opacity: `${Number(opacity) / 100}` }}
size={ICON_SIZE}
/>
<span
className={`leading-none opacity-${opacity}`}
Expand Down Expand Up @@ -209,15 +213,20 @@ const Wrapper = ({ parent, tag }: { parent: HTMLElement; tag: string }) => {
<Button
small
id={id}
className={`roamjs-discourse-context-overlay`}
style={{
minHeight: "initial",
paddingTop: ".25rem",
paddingBottom: ".25rem",
}}
minimal
className={"roamjs-discourse-context-overlay"}
disabled={true}
>
<div className="flex items-center gap-1.5">
<Icon icon={"diagram-tree"} />
<span className="mr-1">-</span>
<Icon icon={"link"} />
<span>-</span>
<Icon icon={"diagram-tree"} size={ICON_SIZE} />
<span className={`mr-1 leading-none`}>-</span>
<Icon icon={"link"} size={ICON_SIZE} />
<span className={`leading-none`}>-</span>
</div>
</Button>
);
Expand Down
6 changes: 4 additions & 2 deletions apps/roam/src/utils/initializeObserversAndListeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import {
addPageRefObserver,
getPageRefObserversSize,
previewPageRefHandler,
overlayPageRefHandler,
getOverlayHandler,
onPageRefObserverChange,
getSuggestiveOverlayHandler,
} from "~/utils/pageRefObserverHandlers";
import getDiscourseNodes from "~/utils/getDiscourseNodes";
Expand Down Expand Up @@ -186,7 +187,8 @@ export const initObservers = async ({
if (onloadArgs.extensionAPI.settings.get("page-preview"))
addPageRefObserver(previewPageRefHandler);
if (onloadArgs.extensionAPI.settings.get("discourse-context-overlay")) {
addPageRefObserver((s) => overlayPageRefHandler(s, onloadArgs));
const overlayHandler = getOverlayHandler(onloadArgs);
onPageRefObserverChange(overlayHandler)(true);
}
if (!!getPageRefObserversSize()) enablePageRefObserver();

Expand Down
68 changes: 67 additions & 1 deletion apps/roam/src/utils/pageRefObserverHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,24 @@ export const overlayPageRefHandler = (
const tag =
s.getAttribute("data-tag") ||
s.parentElement.getAttribute("data-link-title");
const hasOverlayAttribute = s.getAttribute("data-roamjs-discourse-overlay");
const hasOverlayElement =
(s.hasAttribute("data-tag") &&
Array.from(s.children).some(
(child) =>
child instanceof HTMLSpanElement &&
child.querySelector(".roamjs-discourse-context-overlay"),
)) ||
(s.parentElement &&
Array.from(s.parentElement.children).some(
(child) =>
child instanceof HTMLSpanElement &&
child.querySelector(".roamjs-discourse-context-overlay"),
));
if (
tag &&
!s.getAttribute("data-roamjs-discourse-overlay") &&
!hasOverlayAttribute &&
!hasOverlayElement &&
isDiscourseNode(getPageUidByPageTitle(tag))
) {
s.setAttribute("data-roamjs-discourse-overlay", "true");
Expand Down Expand Up @@ -116,13 +131,64 @@ const disablePageRefObserver = () => {
pageRefObserverRef.current = undefined;
};

const applyHandlersToExistingPageRefs = (
handler: (s: HTMLSpanElement) => void,
) => {
const existingPageRefs =
document.querySelectorAll<HTMLSpanElement>("span.rm-page-ref");
existingPageRefs.forEach((pageRef) => {
handler(pageRef);
});
};

const removeOverlaysFromExistingPageRefs = () => {
// Find all page refs that have the overlay attribute OR have overlay elements
// This ensures we catch all cases, even if attribute is missing
const allPageRefs =
document.querySelectorAll<HTMLSpanElement>("span.rm-page-ref");
allPageRefs.forEach((pageRef) => {
// Check if overlay is a direct child of pageRef
const directChildContainer = Array.from(pageRef.children).find(
(child) =>
child instanceof HTMLSpanElement &&
child.querySelector(".roamjs-discourse-context-overlay"),
) as HTMLSpanElement | undefined;
if (directChildContainer) {
directChildContainer.remove();
pageRef.removeAttribute("data-roamjs-discourse-overlay");
return;
}

// Check if overlay is a direct child of pageRef's parent element
if (pageRef.parentElement) {
const parentDirectChildContainer = Array.from(
pageRef.parentElement.children,
).find(
(child) =>
child instanceof HTMLSpanElement &&
child.querySelector(".roamjs-discourse-context-overlay"),
) as HTMLSpanElement | undefined;
if (parentDirectChildContainer) {
parentDirectChildContainer.remove();
pageRef.removeAttribute("data-roamjs-discourse-overlay");
}
}
});
};

export const onPageRefObserverChange =
(handler: (s: HTMLSpanElement) => void) => (b: boolean) => {
if (b) {
if (!pageRefObservers.size) enablePageRefObserver();
pageRefObservers.add(handler);
// Apply handler to existing page refs when enabling
applyHandlersToExistingPageRefs(handler);
} else {
pageRefObservers.delete(handler);
// Remove overlays from existing page refs when disabling
if (handler === cachedHandler) {
removeOverlaysFromExistingPageRefs();
}
if (!pageRefObservers.size) disablePageRefObserver();
}
};
Expand Down
45 changes: 38 additions & 7 deletions apps/roam/src/utils/registerCommandPaletteCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ import getDiscourseNodes from "./getDiscourseNodes";
import fireQuery from "./fireQuery";
import { excludeDefaultNodes } from "~/utils/getDiscourseNodes";
import { render as renderSettings } from "~/components/settings/Settings";
import {
getOverlayHandler,
onPageRefObserverChange,
} from "./pageRefObserverHandlers";

export const registerCommandPaletteCommands = (onloadArgs: OnloadArgs) => {
const { extensionAPI } = onloadArgs;
Expand Down Expand Up @@ -134,18 +138,45 @@ export const registerCommandPaletteCommands = (onloadArgs: OnloadArgs) => {

const renderSettingsPopup = () => renderSettings({ onloadArgs });

const toggleDiscourseContextOverlay = async () => {
const currentValue =
(extensionAPI.settings.get("discourse-context-overlay") as boolean) ??
false;
const newValue = !currentValue;
try {
await extensionAPI.settings.set("discourse-context-overlay", newValue);
} catch (error) {
const e = error as Error;
renderToast({
id: "discourse-context-overlay-toggle-error",
content: `Failed to toggle discourse context overlay: ${e.message}`,
});
return;
}
const overlayHandler = getOverlayHandler(onloadArgs);
onPageRefObserverChange(overlayHandler)(newValue);
renderToast({
id: "discourse-context-overlay-toggle",
content: `Discourse context overlay ${newValue ? "enabled" : "disabled"}`,
});
};

const addCommand = (label: string, callback: () => void) => {
return extensionAPI.ui.commandPalette.addCommand({
label,
callback,
});
};

// Roam organizes commands by alphabetically
addCommand("DG: Export - Current Page", exportCurrentPage);
addCommand("DG: Export - Discourse Graph", exportDiscourseGraph);
addCommand("DG: Open - Discourse Settings", renderSettingsPopup);
addCommand("DG: Open - Query Drawer", openQueryDrawerWithArgs);
addCommand("DG: Query Block - Create", createQueryBlock);
addCommand("DG: Query Block - Refresh", refreshCurrentQueryBuilder);
// Roam organizes commands alphabetically
void addCommand("DG: Export - Current page", exportCurrentPage);
void addCommand("DG: Export - Discourse graph", exportDiscourseGraph);
void addCommand("DG: Open - Discourse settings", renderSettingsPopup);
void addCommand("DG: Open - Query drawer", openQueryDrawerWithArgs);
void addCommand(
"DG: Toggle - Discourse context overlay",
toggleDiscourseContextOverlay,
);
void addCommand("DG: Query block - Create", createQueryBlock);
void addCommand("DG: Query block - Refresh", refreshCurrentQueryBuilder);
};