From e455f23197ba8093feb5239ee08325245ddb3b39 Mon Sep 17 00:00:00 2001 From: Michael Gartner Date: Tue, 17 Feb 2026 16:53:30 -0600 Subject: [PATCH 1/8] add more posthog.capture --- .../src/components/DiscourseFloatingMenu.tsx | 12 +++- apps/roam/src/components/Export.tsx | 64 ++++++++++++++++++- apps/roam/src/components/QueryDrawer.tsx | 2 +- apps/roam/src/components/canvas/Tldraw.tsx | 35 ++++++++++ .../src/components/settings/QuerySettings.tsx | 7 ++ .../roam/src/components/settings/Settings.tsx | 17 ++++- .../utils/registerCommandPaletteCommands.ts | 24 ++++++- 7 files changed, 155 insertions(+), 6 deletions(-) diff --git a/apps/roam/src/components/DiscourseFloatingMenu.tsx b/apps/roam/src/components/DiscourseFloatingMenu.tsx index a3e25b112..07f71b694 100644 --- a/apps/roam/src/components/DiscourseFloatingMenu.tsx +++ b/apps/roam/src/components/DiscourseFloatingMenu.tsx @@ -12,6 +12,7 @@ import { } from "@blueprintjs/core"; import { FeedbackWidget } from "./BirdEatsBugs"; import { render as renderSettings } from "~/components/settings/Settings"; +import posthog from "posthog-js"; type DiscourseFloatingMenuProps = { // CSS placement class @@ -36,6 +37,7 @@ export const DiscourseFloatingMenu = (props: DiscourseFloatingMenuProps) => ( text="Send feedback" icon="send-message" onClick={() => { + posthog.capture("Floating Menu: Feedback Clicked"); try { (window.birdeatsbug as FeedbackWidget | undefined)?.trigger?.(); } catch (error) { @@ -46,6 +48,7 @@ export const DiscourseFloatingMenu = (props: DiscourseFloatingMenuProps) => ( posthog.capture("Floating Menu: Docs Clicked")} href="https://discoursegraphs.com/docs/roam" rel="noopener noreferrer" target="_blank" @@ -53,6 +56,7 @@ export const DiscourseFloatingMenu = (props: DiscourseFloatingMenuProps) => ( posthog.capture("Floating Menu: Community Clicked")} href="https://join.slack.com/t/discoursegraphs/shared_invite/zt-37xklatti-cpEjgPQC0YyKYQWPNgAkEg" rel="noopener noreferrer" target="_blank" @@ -60,12 +64,18 @@ export const DiscourseFloatingMenu = (props: DiscourseFloatingMenuProps) => ( renderSettings({ onloadArgs: props.onloadArgs! })} + onClick={() => { + posthog.capture("Floating Menu: Settings Clicked"); + renderSettings({ onloadArgs: props.onloadArgs! }); + }} rel="noopener noreferrer" target="_blank" /> } + onOpened={() => { + posthog.capture("Floating Menu: Opened"); + }} onClosed={() => { document.getElementById("dg-floating-menu-button")?.blur(); }} diff --git a/apps/roam/src/components/Export.tsx b/apps/roam/src/components/Export.tsx index 5e18802e5..119971087 100644 --- a/apps/roam/src/components/Export.tsx +++ b/apps/roam/src/components/Export.tsx @@ -83,6 +83,7 @@ import getDiscourseRelations, { DiscourseRelation, } from "~/utils/getDiscourseRelations"; import { AddReferencedNodeType } from "./canvas/DiscourseRelationShape/DiscourseRelationTool"; +import posthog from "posthog-js"; const ExportProgress = ({ id }: { id: string }) => { const [progress, setProgress] = useState(0); @@ -153,7 +154,15 @@ const ExportDialog: ExportDialogComponent = ({ const exportId = useMemo(() => nanoid(), []); useEffect(() => { setDialogOpen(isOpen); - }, [isOpen]); + if (isOpen) { + posthog.capture("Export Dialog: Opened", { + title, + resultCount: results.length, + initialPanel: initialPanel ?? "sendTo", + isExportDiscourseGraph, + }); + } + }, [initialPanel, isExportDiscourseGraph, isOpen, results.length, title]); const [dialogOpen, setDialogOpen] = useState(isOpen); const exportTypes = useMemo( () => getExportTypes({ results, exportId, isExportDiscourseGraph }), @@ -651,6 +660,8 @@ const ExportDialog: ExportDialogComponent = ({ let toastContent: React.ReactNode; let uid = selectedPageUid; const title = selectedPageTitle; + const isCanvasDestination = !isSendToGraph && isCanvasPage; + const shouldCreatePage = !isSendToGraph && !isLiveBlock(uid); if (isSendToGraph) { addToGraphOverView(); @@ -687,6 +698,13 @@ const ExportDialog: ExportDialogComponent = ({ ); } + posthog.capture("Results View: Send To Destination", { + destination: isSendToGraph ? "graph" : "page", + isCanvasDestination, + createdPage: shouldCreatePage, + resultCount: results.length, + }); + renderToast({ content: toastContent, intent: "success", @@ -736,9 +754,19 @@ const ExportDialog: ExportDialogComponent = ({ const blob = new Blob([download], { type: "application/zip" }); saveAs(blob, `${filename}.zip`); } + posthog.capture("Export Dialog: Export Completed", { + exportType: "PDF", + destination: activeExportDestination, + fileCount: files.length, + }); onClose(); } catch (e) { setError("Failed to export files."); + posthog.capture("Export Dialog: Export Failed", { + exportType: "PDF", + destination: activeExportDestination, + error: (e as Error).message ?? "unknown error", + }); } }; const ExportPanel = ( @@ -838,6 +866,13 @@ const ExportDialog: ExportDialogComponent = ({ setLoading(true); updateExportProgress({ progress: 0, id: exportId }); setError(""); + posthog.capture("Export Dialog: Export Started", { + exportType: activeExportType, + destination: activeExportDestination, + includeDiscourseContext, + resultCount: results.length, + isExportDiscourseGraph, + }); // eslint-disable-next-line @typescript-eslint/no-misused-promises setTimeout(async () => { try { @@ -879,6 +914,11 @@ const ExportDialog: ExportDialogComponent = ({ content: "Upload Success", intent: "success", }); + posthog.capture("Export Dialog: Export Completed", { + exportType: activeExportType, + destination: activeExportDestination, + fileCount: files.length, + }); onClose(); } } catch (error) { @@ -894,6 +934,11 @@ const ExportDialog: ExportDialogComponent = ({ type: "text/plain;charset=utf-8", }); saveAs(blob, title); + posthog.capture("Export Dialog: Export Completed", { + exportType: activeExportType, + destination: activeExportDestination, + fileCount: files.length, + }); onClose(); return; } @@ -906,12 +951,22 @@ const ExportDialog: ExportDialogComponent = ({ ); void zip.generateAsync({ type: "blob" }).then((content) => { saveAs(content, `${filename}.zip`); + posthog.capture("Export Dialog: Export Completed", { + exportType: activeExportType, + destination: activeExportDestination, + fileCount: files.length, + }); onClose(); }); } else { setError(`Unsupported export type: ${exportType}`); } } catch (e) { + posthog.capture("Export Dialog: Export Failed", { + exportType: activeExportType, + destination: activeExportDestination, + error: (e as Error).message ?? "unknown error", + }); internalError({ error: e as Error, type: "Export Dialog Failed", @@ -1007,7 +1062,12 @@ const ExportDialog: ExportDialogComponent = ({ id="export-tabs" large={true} selectedTabId={selectedTabId} - onChange={(newTabId: string) => setSelectedTabId(newTabId)} + onChange={(newTabId: string) => { + setSelectedTabId(newTabId); + posthog.capture("Export Dialog: Tab Opened", { + tabId: newTabId, + }); + }} > diff --git a/apps/roam/src/components/QueryDrawer.tsx b/apps/roam/src/components/QueryDrawer.tsx index 3f11740a5..f7913e8cd 100644 --- a/apps/roam/src/components/QueryDrawer.tsx +++ b/apps/roam/src/components/QueryDrawer.tsx @@ -380,7 +380,7 @@ const QueryDrawer = ({ ); export const openQueryDrawer = (onloadArgs: OnloadArgs) => { - posthog.capture("Query Drawer: Opened", {}); + posthog.capture("Query Drawer: Opened"); return Promise.resolve( getPageUidByPageTitle("roam/js/query-builder/drawer") || createPage({ diff --git a/apps/roam/src/components/canvas/Tldraw.tsx b/apps/roam/src/components/canvas/Tldraw.tsx index 4008eb2fe..7729a7f57 100644 --- a/apps/roam/src/components/canvas/Tldraw.tsx +++ b/apps/roam/src/components/canvas/Tldraw.tsx @@ -98,6 +98,7 @@ import { TLRecord } from "@tldraw/tlschema"; import { WHITE_LOGO_SVG } from "~/icons"; import { BLOCK_REF_REGEX } from "roamjs-components/dom"; import { defaultHandleExternalTextContent } from "./defaultHandleExternalTextContent"; +import posthog from "posthog-js"; declare global { // eslint-disable-next-line @typescript-eslint/consistent-type-definitions @@ -172,12 +173,14 @@ const TldrawCanvas = ({ title }: { title: string }) => { if (wrapper) wrapper.classList.add("dg-tldraw-maximized"); tldrawEl.classList.add("absolute", "inset-0"); tldrawEl.classList.remove("relative"); + posthog.capture("Canvas: Fullscreen Toggled", { maximized: true }); updateViewportScreenBounds(tldrawEl); } else { // Going back to normal if (wrapper) wrapper.classList.remove("dg-tldraw-maximized"); tldrawEl.classList.add("relative"); tldrawEl.classList.remove("absolute", "inset-0"); + posthog.capture("Canvas: Fullscreen Toggled", { maximized: false }); updateViewportScreenBounds(tldrawEl); } }; @@ -347,6 +350,10 @@ const TldrawCanvas = ({ title }: { title: string }) => { // Check if we have a target shape if (shapeAtPoint && isDiscourseNodeShape(shapeAtPoint)) { + posthog.capture("Canvas: Relation Created", { + relationType: relationShape.type, + toolType: relationCreationRef.current.toolType || "", + }); // We have a valid target, call the relation creation method const util = app.getShapeUtil(relationShape); if ( @@ -420,6 +427,7 @@ const TldrawCanvas = ({ title }: { title: string }) => { const uid = e.dataTransfer.getData("application/x-roam-uid"); if (!uid || !appRef.current || !extensionAPI) return; + posthog.capture("Canvas: Roam Block Dropped"); // Use the text content handler to process ((uid)) - this will handle both // creating discourse nodes and falling back to text shapes @@ -501,6 +509,12 @@ const TldrawCanvas = ({ title }: { title: string }) => { // STORE const pageUid = useMemo(() => getPageUidByPageTitle(title), [title]); + useEffect(() => { + posthog.capture("Canvas: Opened", { + pageUid, + inSidebar: !!containerRef.current?.closest(".rm-sidebar-outline"), + }); + }, [pageUid]); const arrowShapeMigrations = useMemo( () => createMigrations({ @@ -723,6 +737,9 @@ const TldrawCanvas = ({ title }: { title: string }) => { if (e.shiftKey) { if (app.getSelectedShapes().length > 1) return; // User is selecting multiple shapes + posthog.capture("Canvas: Open Node", { + source: "sidebar", + }); void openBlockInSidebar(shapeUid); app.selectNone(); } @@ -732,6 +749,10 @@ const TldrawCanvas = ({ title }: { title: string }) => { // || e.metaKey ) { const isPage = !!getPageTitleByPageUid(shapeUid); + posthog.capture("Canvas: Open Node", { + source: "main-window", + isPage, + }); if (isPage) { void window.roamAlphaAPI.ui.mainWindow.openPage({ page: { uid: shapeUid }, @@ -904,6 +925,9 @@ const InsideEditorAndUiContext = ({ nodeType: nodeType.type, content, }); + posthog.capture("Canvas: Node Added from External Content", { + source: "page-reference", + }); return; } @@ -923,6 +947,9 @@ const InsideEditorAndUiContext = ({ nodeType: "blck-node", content, }); + posthog.capture("Canvas: Node Added from External Content", { + source: "block-reference", + }); } catch (error) { await callDefaultTextHandler(content); } @@ -978,6 +1005,10 @@ const InsideEditorAndUiContext = ({ y: position.y - size.h / 2, props: { assetId, w: size.w, h: size.h }, }); + posthog.capture("Canvas: Asset Added", { + source: "file-drop", + mimeType: file.type, + }); return asset; }, @@ -1054,6 +1085,10 @@ const InsideEditorAndUiContext = ({ y: position.y - height / 2, props: { assetId, w: width, h: height }, }); + posthog.capture("Canvas: Asset Added", { + source: "svg-paste", + mimeType: "image/svg+xml", + }); return asset; }, diff --git a/apps/roam/src/components/settings/QuerySettings.tsx b/apps/roam/src/components/settings/QuerySettings.tsx index 459896141..e40613c0c 100644 --- a/apps/roam/src/components/settings/QuerySettings.tsx +++ b/apps/roam/src/components/settings/QuerySettings.tsx @@ -10,6 +10,7 @@ import { PersonalNumberPanel, PersonalMultiTextPanel, } from "./components/BlockPropSettingPanels"; +import posthog from "posthog-js"; const QuerySettings = ({ extensionAPI, @@ -27,6 +28,9 @@ const QuerySettings = ({ } onChange={(checked) => { void extensionAPI.settings.set(HIDE_METADATA_KEY, checked); + posthog.capture("Query Settings: Hide Metadata Toggled", { + hidden: checked, + }); }} /> { void extensionAPI.settings.set(DEFAULT_PAGE_SIZE_KEY, value); + posthog.capture("Query Settings: Default Page Size Changed", { + value, + }); }} /> {