From 4a9d243c4c21af5dc5dc0d09210d552d9b13b4c7 Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Tue, 3 Mar 2026 01:10:28 -0500 Subject: [PATCH 1/2] wip --- .../src/components/canvas/DiscourseNodeUtil.tsx | 4 +++- .../DiscourseRelationTool.tsx | 7 +++++-- .../DiscourseRelationUtil.tsx | 3 ++- .../src/components/canvas/DiscourseToolPanel.tsx | 5 ++++- apps/roam/src/components/canvas/Tldraw.tsx | 1 + apps/roam/src/components/canvas/toolLock.ts | 13 +++++++++++++ 6 files changed, 28 insertions(+), 5 deletions(-) create mode 100644 apps/roam/src/components/canvas/toolLock.ts diff --git a/apps/roam/src/components/canvas/DiscourseNodeUtil.tsx b/apps/roam/src/components/canvas/DiscourseNodeUtil.tsx index fc2f1d742..dccab2379 100644 --- a/apps/roam/src/components/canvas/DiscourseNodeUtil.tsx +++ b/apps/roam/src/components/canvas/DiscourseNodeUtil.tsx @@ -43,6 +43,7 @@ import { getSetting } from "~/utils/extensionSettings"; import DiscourseContextOverlay from "~/components/DiscourseContextOverlay"; import { getDiscourseNodeColors } from "~/utils/getDiscourseNodeColors"; import { render as renderToast } from "roamjs-components/components/Toast"; +import { setCurrentToolToSelectIfUnlocked } from "./toolLock"; // TODO REPLACE WITH TLDRAW DEFAULTS // https://github.com/tldraw/tldraw/pull/1580/files @@ -106,6 +107,7 @@ export const createNodeShapeTools = ( return class DiscourseNodeTool extends StateNode { static id = n.type; static initial = "idle"; + static isLockable = true; shapeType = n.type; override onEnter = () => { @@ -126,7 +128,7 @@ export const createNodeShapeTools = ( props: { fontFamily: "sans", size: "s" }, }); this.editor.setEditingShape(shapeId); - this.editor.setCurrentTool("select"); + setCurrentToolToSelectIfUnlocked(this.editor); }; }; }); diff --git a/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationTool.tsx b/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationTool.tsx index 3e809406b..b3198634b 100644 --- a/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationTool.tsx +++ b/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationTool.tsx @@ -11,6 +11,7 @@ import { } from "./DiscourseRelationUtil"; import { discourseContext } from "~/components/canvas/Tldraw"; import { dispatchToastEvent } from "~/components/canvas/ToastListener"; +import { setCurrentToolToSelectIfUnlocked } from "~/components/canvas/toolLock"; export type AddReferencedNodeType = Record; type ReferenceFormatType = { @@ -28,6 +29,7 @@ export const createAllReferencedNodeTools = ( class ReferencedNodeTool extends StateNode { static override initial = "idle"; static override id = action; + static override isLockable = true; static override children = (): TLStateNodeConstructor[] => [ this.Idle, this.Pointing, @@ -278,7 +280,7 @@ export const createAllReferencedNodeTools = ( }; override onCancel = () => { - this.editor.setCurrentTool("select"); + setCurrentToolToSelectIfUnlocked(this.editor); }; override onKeyUp: TLEventHandlers["onKeyUp"] = (info) => { @@ -315,6 +317,7 @@ export const createAllRelationShapeTools = ( class RelationShapeTool extends StateNode { static override initial = "idle"; static override id = name; + static override isLockable = true; static override children = (): TLStateNodeConstructor[] => [ this.Idle, this.Pointing, @@ -574,7 +577,7 @@ export const createAllRelationShapeTools = ( }; override onCancel = () => { - this.editor.setCurrentTool("select"); + setCurrentToolToSelectIfUnlocked(this.editor); }; override onKeyUp: TLEventHandlers["onKeyUp"] = (info) => { diff --git a/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx b/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx index 95305bb92..56a220509 100644 --- a/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx +++ b/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx @@ -108,6 +108,7 @@ import { AddReferencedNodeType } from "./DiscourseRelationTool"; import { dispatchToastEvent } from "~/components/canvas/ToastListener"; import internalError from "~/utils/internalError"; import { USE_REIFIED_RELATIONS } from "~/data/userSettings"; +import { setCurrentToolToSelectIfUnlocked } from "~/components/canvas/toolLock"; const COLOR_ARRAY = Array.from(DefaultColorStyle.values) .filter((c) => !["red", "green", "grey"].includes(c)) @@ -1084,7 +1085,7 @@ export class BaseDiscourseRelationUtil extends ShapeUtil static override props = arrowShapeProps; cancelAndWarn = (title: string) => { - this.editor.setCurrentTool("select"); + setCurrentToolToSelectIfUnlocked(this.editor); dispatchToastEvent({ id: `tldraw-cancel-and-warn-${title}`, title, diff --git a/apps/roam/src/components/canvas/DiscourseToolPanel.tsx b/apps/roam/src/components/canvas/DiscourseToolPanel.tsx index 5241973bb..c342a8ca2 100644 --- a/apps/roam/src/components/canvas/DiscourseToolPanel.tsx +++ b/apps/roam/src/components/canvas/DiscourseToolPanel.tsx @@ -16,6 +16,7 @@ import { TOOL_ARROW_ICON_SVG, NODE_COLOR_ICON_SVG } from "~/icons"; import { getDiscourseNodeColors } from "~/utils/getDiscourseNodeColors"; import { DEFAULT_WIDTH, DEFAULT_HEIGHT } from "./Tldraw"; import { DEFAULT_STYLE_PROPS, FONT_SIZES } from "./DiscourseNodeUtil"; +import { lockToolIfNeeded, setCurrentToolToSelectIfUnlocked } from "./toolLock"; export type DiscourseGraphPanelProps = { nodes: DiscourseNode[]; @@ -167,6 +168,7 @@ const DiscourseGraphPanel = ({ const itemIndex = target.dataset.drag_item_index!; const item = panelItems[+itemIndex]; if (item) { + lockToolIfNeeded(editor); editor.setCurrentTool(item.id); } dragState.set({ @@ -190,9 +192,10 @@ const DiscourseGraphPanel = ({ props: { fontFamily: "sans", size: "s" }, }); editor.setEditingShape(shapeId); - editor.setCurrentTool("select"); + setCurrentToolToSelectIfUnlocked(editor); } else { // For relations, just activate the tool + lockToolIfNeeded(editor); editor.setCurrentTool(current.item.id); } diff --git a/apps/roam/src/components/canvas/Tldraw.tsx b/apps/roam/src/components/canvas/Tldraw.tsx index 16b855556..f64b43db9 100644 --- a/apps/roam/src/components/canvas/Tldraw.tsx +++ b/apps/roam/src/components/canvas/Tldraw.tsx @@ -730,6 +730,7 @@ const TldrawCanvasShared = ({ const discourseGraphTool = class DiscourseGraphTool extends StateNode { static override id = "discourse-tool"; static override initial = "idle"; + static override isLockable = true; }; const discourseNodeTools = createNodeShapeTools(allNodes); const discourseRelationTools = createAllRelationShapeTools(allRelationNames); diff --git a/apps/roam/src/components/canvas/toolLock.ts b/apps/roam/src/components/canvas/toolLock.ts new file mode 100644 index 000000000..b6341c68b --- /dev/null +++ b/apps/roam/src/components/canvas/toolLock.ts @@ -0,0 +1,13 @@ +import { Editor } from "tldraw"; + +export const setCurrentToolToSelectIfUnlocked = (editor: Editor): void => { + if (!editor.getInstanceState().isToolLocked) { + editor.setCurrentTool("select"); + } +}; + +export const lockToolIfNeeded = (editor: Editor): void => { + if (!editor.getInstanceState().isToolLocked) { + editor.updateInstanceState({ isToolLocked: true }); + } +}; \ No newline at end of file From 81fc1d330db54f07ae36df32699847532e02a337 Mon Sep 17 00:00:00 2001 From: Trang Doan Date: Tue, 3 Mar 2026 01:12:45 -0500 Subject: [PATCH 2/2] lint --- .../canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx | 5 ++++- apps/roam/src/components/canvas/toolLock.ts | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx b/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx index 56a220509..d4bec8a28 100644 --- a/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx +++ b/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx @@ -86,7 +86,10 @@ const getCanvasPageUidFromDOM = (): string | null => { // With multiple canvases, find the one that contains the currently focused element // or has been recently interacted with (has :focus-within) for (const container of containers) { - if (container.matches(":focus-within") || container.contains(document.activeElement)) { + if ( + container.matches(":focus-within") || + container.contains(document.activeElement) + ) { return container.getAttribute("data-page-uid"); } } diff --git a/apps/roam/src/components/canvas/toolLock.ts b/apps/roam/src/components/canvas/toolLock.ts index b6341c68b..ce6224a07 100644 --- a/apps/roam/src/components/canvas/toolLock.ts +++ b/apps/roam/src/components/canvas/toolLock.ts @@ -10,4 +10,4 @@ export const lockToolIfNeeded = (editor: Editor): void => { if (!editor.getInstanceState().isToolLocked) { editor.updateInstanceState({ isToolLocked: true }); } -}; \ No newline at end of file +};