diff --git a/apps/roam/src/components/Export.tsx b/apps/roam/src/components/Export.tsx index 928e459e1..cbe721e9b 100644 --- a/apps/roam/src/components/Export.tsx +++ b/apps/roam/src/components/Export.tsx @@ -62,7 +62,8 @@ import { } from "@tldraw/editor"; import calcCanvasNodeSizeAndImg from "~/utils/calcCanvasNodeSizeAndImg"; import { - createNodeShapeUtils, + DiscourseNodeUtil, + DISCOURSE_NODE_SHAPE_TYPE, DiscourseNodeShape, } from "~/components/canvas/DiscourseNodeUtil"; import { discourseContext, MAX_WIDTH } from "~/components/canvas/Tldraw"; @@ -360,7 +361,7 @@ const ExportDialog: ExportDialogComponent = ({ ); // UTILS - const discourseNodeUtils = createNodeShapeUtils(allNodes); + const discourseNodeUtils = [DiscourseNodeUtil]; const discourseRelationUtils = createAllRelationShapeUtils(allRelationIds); const referencedNodeUtils = createAllReferencedNodeUtils( @@ -551,12 +552,13 @@ const ExportDialog: ExportDialogComponent = ({ index: currentIndex, rotation: 0, isLocked: false, - type: nodeType, + type: DISCOURSE_NODE_SHAPE_TYPE, props: { w, h, uid: r.uid, title: String(r[firstColumnKey]), + nodeTypeId: nodeType, imageUrl, size: "s", fontFamily: "sans", diff --git a/apps/roam/src/components/canvas/Clipboard.tsx b/apps/roam/src/components/canvas/Clipboard.tsx index fcd86b194..1d4d7f3fa 100644 --- a/apps/roam/src/components/canvas/Clipboard.tsx +++ b/apps/roam/src/components/canvas/Clipboard.tsx @@ -49,6 +49,7 @@ import getAllReferencesOnPage from "~/utils/getAllReferencesOnPage"; import { DiscourseNodeShape, DEFAULT_STYLE_PROPS, + DISCOURSE_NODE_SHAPE_TYPE, FONT_SIZES, } from "./DiscourseNodeUtil"; import { openBlockInSidebar, createBlock } from "roamjs-components/writes"; @@ -702,7 +703,7 @@ const ClipboardPageSection = ({ const shapeId = createShapeId(); const shape = { id: shapeId, - type: nodeType.type, + type: DISCOURSE_NODE_SHAPE_TYPE, x: pagePoint.x - w / 2, y: pagePoint.y - h / 2, props: { @@ -713,6 +714,7 @@ const ClipboardPageSection = ({ imageUrl, size: "s" as TLDefaultSizeStyle, fontFamily: "sans" as TLDefaultFontStyle, + nodeTypeId: nodeType.type, }, }; editor.createShape(shape); diff --git a/apps/roam/src/components/canvas/DiscourseNodeUtil.tsx b/apps/roam/src/components/canvas/DiscourseNodeUtil.tsx index 4c1cb211a..bcf52d6d7 100644 --- a/apps/roam/src/components/canvas/DiscourseNodeUtil.tsx +++ b/apps/roam/src/components/canvas/DiscourseNodeUtil.tsx @@ -4,7 +4,6 @@ import { TLBaseShape, useEditor, DefaultColorStyle, - Editor, createShapeId, TLDefaultHorizontalAlignStyle, TLDefaultVerticalAlignStyle, @@ -16,6 +15,7 @@ import { DefaultSizeStyle, T, FONT_FAMILIES, + TLShape, TLDefaultFontStyle, DefaultFontStyle, toDomPrecision, @@ -94,6 +94,8 @@ export const COLOR_PALETTE: Record = { }; /* eslint-disable @typescript-eslint/naming-convention */ +export const DISCOURSE_NODE_SHAPE_TYPE = "discourse-node"; + const getRelationIds = () => new Set( Object.values(discourseContext.relations).flatMap((rs) => @@ -109,7 +111,7 @@ export const createNodeShapeTools = ( static id = n.type; static initial = "idle"; static isLockable = true; - shapeType = n.type; + nodeTypeId = n.type; override onEnter = () => { this.editor.setCursor({ @@ -123,10 +125,14 @@ export const createNodeShapeTools = ( const shapeId = createShapeId(); this.editor.createShape({ id: shapeId, - type: this.shapeType, + type: DISCOURSE_NODE_SHAPE_TYPE, x: currentPagePoint.x, y: currentPagePoint.y, - props: { fontFamily: "sans", size: "s" }, + props: { + fontFamily: "sans", + size: "s", + nodeTypeId: this.nodeTypeId, + }, }); this.editor.setEditingShape(shapeId); }; @@ -134,23 +140,24 @@ export const createNodeShapeTools = ( }); }; -export const createNodeShapeUtils = (nodes: DiscourseNode[]) => { - return nodes.map((node) => { - class DiscourseNodeUtil extends BaseDiscourseNodeUtil { - constructor(editor: Editor) { - super(editor, node.type); - } - static override type = node.type; // removing this gives undefined error - // getDefaultProps(): DiscourseNodeShape["props"] { - // const baseProps = super.getDefaultProps(); - // return { - // ...baseProps, - // color: node.color, - // }; - // } - } - return DiscourseNodeUtil; - }); +type ShapeWithOptionalNodeTypeId = TLShape & { + props?: { + nodeTypeId?: string; + }; +}; + +export const getDiscourseNodeTypeId = ({ + shape, +}: { + shape: ShapeWithOptionalNodeTypeId; +}): string => { + return shape.props?.nodeTypeId || shape.type; +}; + +export const isDiscourseNodeShape = ( + shape: TLShape, +): shape is DiscourseNodeShape => { + return shape.type === DISCOURSE_NODE_SHAPE_TYPE; }; export type DiscourseNodeShape = TLBaseShape< @@ -161,18 +168,14 @@ export type DiscourseNodeShape = TLBaseShape< // opacity: TLOpacityType; uid: string; title: string; + nodeTypeId: string; imageUrl?: string; size: TLDefaultSizeStyle; fontFamily: TLDefaultFontStyle; } >; -export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil { - type: string; - - constructor(editor: Editor, type: string) { - super(editor); - this.type = type; - } +export class DiscourseNodeUtil extends BaseBoxShapeUtil { + static override type = DISCOURSE_NODE_SHAPE_TYPE; static override props = { w: T.number, @@ -180,6 +183,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil // opacity: T.number, uid: T.string, title: T.string, + nodeTypeId: T.string, imageUrl: T.optional(T.string), size: DefaultSizeStyle, fontFamily: DefaultFontStyle, @@ -196,6 +200,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil h: 64, uid: window.roamAlphaAPI.util.generateUID(), title: "", + nodeTypeId: "", size: "s", fontFamily: "sans", }; @@ -241,7 +246,11 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil const nodesInCanvas = Object.fromEntries( allRecords .filter((r): r is DiscourseNodeShape => { - return r.typeName === "shape" && nodeIds.has(r.type); + if (r.typeName !== "shape") return false; + const nodeTypeId = getDiscourseNodeTypeId({ shape: r }); + return ( + r.typeName === "shape" && !!nodeTypeId && nodeIds.has(nodeTypeId) + ); }) .map((r) => [r.props.uid, r] as const), ); @@ -332,12 +341,14 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil editor.createShapes(shapesToCreate).createBindings(bindingsToCreate); } - getColors() { - return getDiscourseNodeColors({ nodeType: this.type }); + getColors(shape: DiscourseNodeShape) { + return getDiscourseNodeColors({ + nodeType: getDiscourseNodeTypeId({ shape }), + }); } async toSvg(shape: DiscourseNodeShape): Promise { - const { backgroundColor, textColor } = this.getColors(); + const { backgroundColor, textColor } = this.getColors(shape); const padding = Number(DEFAULT_STYLE_PROPS.padding.replace("px", "")); const props = shape.props; const bounds = new Box(0, 0, props.w, props.h); @@ -434,7 +445,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil const extensionAPI = useExtensionAPI(); const { canvasSettings: { alias = "", "key-image": isKeyImage = "" } = {}, - } = discourseContext.nodes[shape.type] || {}; + } = discourseContext.nodes[getDiscourseNodeTypeId({ shape })] || {}; // eslint-disable-next-line react-hooks/rules-of-hooks const isOverlayEnabled = useMemo( () => getPersonalSetting([PERSONAL_KEYS.overlayInCanvas]), @@ -450,7 +461,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil // Detect discourse node tags in block text for blck-node shapes // eslint-disable-next-line react-hooks/rules-of-hooks const matchedNodeForConversion = useMemo(() => { - if (shape.type !== "blck-node") return null; + if (getDiscourseNodeTypeId({ shape }) !== "blck-node") return null; if (!isLiveBlock(shape.props.uid)) return null; const blockText = getTextByBlockUid(shape.props.uid); if (!blockText) return null; @@ -471,9 +482,9 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil } } return null; - }, [shape.type, shape.props.uid]); + }, [shape]); - const { backgroundColor, textColor } = this.getColors(); + const { backgroundColor, textColor } = this.getColors(shape); const showEmbeddedRoamBlock = !isPageUid(shape.props.uid) && isLiveBlock(shape.props.uid); @@ -492,7 +503,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil const { h, w, imageUrl } = await calcCanvasNodeSizeAndImg({ nodeText: text, uid, - nodeType: this.type, + nodeType: getDiscourseNodeTypeId({ shape }), extensionAPI, }); this.updateProps(shape.id, shape.type, { h, w, imageUrl }); @@ -513,7 +524,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil renderModifyNodeDialog({ mode: isCreating ? "create" : "edit", - nodeType: shape.type, + nodeType: getDiscourseNodeTypeId({ shape }), initialValue: { text: shape.props.title, uid: shape.props.uid }, // Only pass it when editing an existing node that has a valid Roam block UID sourceBlockUid: @@ -538,6 +549,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil this.updateProps(shape.id, shape.type, { title: text, uid, + nodeTypeId: getDiscourseNodeTypeId({ shape }), }); const autoCanvasRelations = getPersonalSetting([ @@ -660,7 +672,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil }); editor.createShapes([ { - type: node.type, + type: DISCOURSE_NODE_SHAPE_TYPE, id: createShapeId(), props: { uid, @@ -670,6 +682,7 @@ export class BaseDiscourseNodeUtil extends BaseBoxShapeUtil imageUrl: nodeImageUrl, fontFamily: "sans", size: "s", + nodeTypeId: node.type, }, x, y, diff --git a/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationTool.tsx b/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationTool.tsx index 801ce1cff..933e2ed69 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 { getDiscourseNodeTypeId } from "~/components/canvas/DiscourseNodeUtil"; export type AddReferencedNodeType = Record; type ReferenceFormatType = { @@ -78,7 +79,10 @@ export const createAllReferencedNodeTools = ( const sourceType = allAddReferencedNodeByAction[action][0].sourceType; const sourceName = allAddReferencedNodeByAction[action][0].sourceName; - if (target?.type === sourceType) { + if ( + target && + getDiscourseNodeTypeId({ shape: target }) === sourceType + ) { this.shapeType = `${action}`; } else { this.cancelAndWarn(`Starting node must be one of ${sourceName}`); @@ -360,8 +364,13 @@ export const createAllRelationShapeTools = ( // } ); + const targetNodeTypeId = target + ? getDiscourseNodeTypeId({ shape: target }) + : undefined; const relation = discourseContext.relations[name].find( - (r) => r.source === target?.type || r.destination === target?.type, + (r) => + r.source === targetNodeTypeId || + r.destination === targetNodeTypeId, ); if (relation) { this.shapeType = relation.id; diff --git a/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx b/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx index 69fee29d3..707841f01 100644 --- a/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx +++ b/apps/roam/src/components/canvas/DiscourseRelationShape/DiscourseRelationUtil.tsx @@ -104,8 +104,10 @@ import createPage from "roamjs-components/writes/createPage"; import openBlockInSidebar from "roamjs-components/writes/openBlockInSidebar"; import triplesToBlocks from "~/utils/triplesToBlocks"; import { - BaseDiscourseNodeUtil, + DiscourseNodeUtil, DiscourseNodeShape, + getDiscourseNodeTypeId, + isDiscourseNodeShape as isDiscourseNodeShapeTypeGuard, } from "~/components/canvas/DiscourseNodeUtil"; import { checkConnectionType } from "~/components/canvas/canvasUtils"; import getPageTitleByPageUid from "roamjs-components/queries/getPageTitleByPageUid"; @@ -148,8 +150,7 @@ export const createAllReferencedNodeUtils = ( static override type = action; isDiscourseNodeShape(shape: TLShape): shape is DiscourseNodeShape { - const shapeUtil = this.editor.getShapeUtil(shape.type); - return shapeUtil instanceof BaseDiscourseNodeUtil; + return isDiscourseNodeShapeTypeGuard(shape); } handleCreateRelationsInRoam = async ({ @@ -179,7 +180,11 @@ export const createAllReferencedNodeUtils = ( const possibleTargets = allAddReferencedNodeByAction[arrow.type].map( (action) => action.destinationType, ); - if (!possibleTargets.includes(target.type)) { + if ( + !possibleTargets.includes( + getDiscourseNodeTypeId({ shape: target }) || "", + ) + ) { return deleteAndWarn( `Target node must be of type ${possibleTargets .map((t) => discourseContext.nodes[t].text) @@ -589,7 +594,7 @@ const asDiscourseNodeShape = ( editor: Editor, ): DiscourseNodeShape | null => { const shapeUtil = editor.getShapeUtil(shape.type); - return shapeUtil instanceof BaseDiscourseNodeUtil + return shapeUtil instanceof DiscourseNodeUtil ? (shape as DiscourseNodeShape) : null; }; @@ -627,8 +632,8 @@ export const createAllRelationShapeUtils = ( const relation = relations.find((r) => r.id === arrow.type); if (!relation) return; - const sourceNodeType = source.type; - const targetNodeType = target.type; + const sourceNodeType = getDiscourseNodeTypeId({ shape: source }); + const targetNodeType = getDiscourseNodeTypeId({ shape: target }); // Check all relations with the same label for a match const { @@ -892,8 +897,10 @@ export const createAllRelationShapeUtils = ( ) { const sourceNodeId = otherBinding.toId; const sourceNode = this.editor.getShape(sourceNodeId); - const targetNodeType = target.type; - const sourceNodeType = sourceNode?.type; + const targetNodeType = getDiscourseNodeTypeId({ shape: target }); + const sourceNodeType = sourceNode + ? getDiscourseNodeTypeId({ shape: sourceNode }) + : undefined; if (sourceNodeType && targetNodeType && shape.type) { const isValidConnection = this.isValidNodeConnection( @@ -997,8 +1004,10 @@ export const createAllRelationShapeUtils = ( const endNode = this.editor.getShape(newBindings.end.toId); if (startNode && endNode) { - const startNodeType = startNode.type; - const endNodeType = endNode.type; + const startNodeType = getDiscourseNodeTypeId({ + shape: startNode, + }); + const endNodeType = getDiscourseNodeTypeId({ shape: endNode }); const { isReverse, matchingRelation } = this.checkConnectionTypeAcrossLabel( diff --git a/apps/roam/src/components/canvas/DiscourseRelationShape/discourseRelationMigrations.ts b/apps/roam/src/components/canvas/DiscourseRelationShape/discourseRelationMigrations.ts index 7584dea72..bece10b5c 100644 --- a/apps/roam/src/components/canvas/DiscourseRelationShape/discourseRelationMigrations.ts +++ b/apps/roam/src/components/canvas/DiscourseRelationShape/discourseRelationMigrations.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ @@ -14,6 +15,7 @@ import { import { createMigrationIds } from "tldraw"; import { RelationBinding } from "./DiscourseRelationBindings"; import { getRelationColor } from "./DiscourseRelationUtil"; +import { DISCOURSE_NODE_SHAPE_TYPE } from "~/components/canvas/DiscourseNodeUtil"; const SEQUENCE_ID_BASE = "com.roam-research.discourse-graphs"; @@ -35,6 +37,7 @@ export const createMigrations = ({ "2.3.0": 2, AddSizeAndFontFamily: 3, RemoveNullAssetFileSize: 4, + MigrateNodeTypeToDiscourseNode: 5, }); return createMigrationSequence({ sequenceId: `${SEQUENCE_ID_BASE}`, @@ -156,6 +159,16 @@ export const createMigrations = ({ delete asset.props.fileSize; }, }, + { + id: versions["MigrateNodeTypeToDiscourseNode"], + scope: "record", + filter: (r: any) => + r.typeName === "shape" && allNodeTypes.includes(r.type), + up: (shape: any) => { + shape.props.nodeTypeId = shape.type; + shape.type = DISCOURSE_NODE_SHAPE_TYPE; + }, + }, ], }); }; diff --git a/apps/roam/src/components/canvas/DiscourseToolPanel.tsx b/apps/roam/src/components/canvas/DiscourseToolPanel.tsx index c30dbc714..e9fda93f8 100644 --- a/apps/roam/src/components/canvas/DiscourseToolPanel.tsx +++ b/apps/roam/src/components/canvas/DiscourseToolPanel.tsx @@ -15,7 +15,11 @@ import { useAtom } from "@tldraw/state-react"; 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 { + DEFAULT_STYLE_PROPS, + DISCOURSE_NODE_SHAPE_TYPE, + FONT_SIZES, +} from "./DiscourseNodeUtil"; export type DiscourseGraphPanelProps = { nodes: DiscourseNode[]; @@ -184,10 +188,14 @@ const DiscourseGraphPanel = ({ const shapeId = createShapeId(); editor.createShape({ id: shapeId, - type: current.item.id, + type: DISCOURSE_NODE_SHAPE_TYPE, x: pagePoint.x - offsetX, y: pagePoint.y - offsetY, - props: { fontFamily: "sans", size: "s" }, + props: { + fontFamily: "sans", + size: "s", + nodeTypeId: current.item.id, + }, }); editor.setEditingShape(shapeId); } else { diff --git a/apps/roam/src/components/canvas/Tldraw.tsx b/apps/roam/src/components/canvas/Tldraw.tsx index 92ce8ae74..4fe6db681 100644 --- a/apps/roam/src/components/canvas/Tldraw.tsx +++ b/apps/roam/src/components/canvas/Tldraw.tsx @@ -38,7 +38,6 @@ import { TLAssetId, getHashForString, TLShapeId, - TLShape, TLStore, TLStoreWithStatus, useToasts, @@ -67,9 +66,10 @@ import { createUiOverrides, } from "./uiOverrides"; import { - BaseDiscourseNodeUtil, + DiscourseNodeUtil, DiscourseNodeShape, createNodeShapeTools, + DISCOURSE_NODE_SHAPE_TYPE, } from "./DiscourseNodeUtil"; import { useRoamStore } from "./useRoamStore"; import { @@ -847,13 +847,14 @@ const TldrawCanvasShared = ({ if (nodeType) { app.createShapes([ { - type: nodeType.type, + type: DISCOURSE_NODE_SHAPE_TYPE, id: createShapeId(), props: { uid: e.detail.uid, title: e.detail.val, size: "s", fontFamily: "sans", + nodeTypeId: nodeType.type, }, ...position, }, @@ -1210,7 +1211,7 @@ const InsideEditorAndUiContext = ({ editor.createShapes([ { id: createShapeId(), - type: nodeType, + type: DISCOURSE_NODE_SHAPE_TYPE, x: position.x - w / 2, y: position.y - h / 2, props: { @@ -1221,6 +1222,7 @@ const InsideEditorAndUiContext = ({ ...(imageUrl && { imageUrl }), size: "s", fontFamily: "sans", + nodeTypeId: nodeType, }, }, ]); @@ -1488,7 +1490,7 @@ const InsideEditorAndUiContext = ({ const removeAfterCreateHandler = editor.sideEffects.registerAfterCreateHandler("shape", (shape) => { const util = editor.getShapeUtil(shape); - if (util instanceof BaseDiscourseNodeUtil) { + if (util instanceof DiscourseNodeUtil) { const autoCanvasRelations = getPersonalSetting([ PERSONAL_KEYS.autoCanvasRelations, ]); diff --git a/apps/roam/src/components/canvas/canvasUtils.ts b/apps/roam/src/components/canvas/canvasUtils.ts index 8f8845cac..5bb71dbbc 100644 --- a/apps/roam/src/components/canvas/canvasUtils.ts +++ b/apps/roam/src/components/canvas/canvasUtils.ts @@ -1,6 +1,6 @@ import { Editor, TLShape } from "tldraw"; import { - BaseDiscourseNodeUtil, + DiscourseNodeUtil, DiscourseNodeShape, } from "~/components/canvas/DiscourseNodeUtil"; import { discourseContext } from "~/components/canvas/Tldraw"; @@ -10,7 +10,7 @@ export const isDiscourseNodeShape = ( shape: TLShape, ): shape is DiscourseNodeShape => { try { - return editor.getShapeUtil(shape) instanceof BaseDiscourseNodeUtil; + return editor.getShapeUtil(shape) instanceof DiscourseNodeUtil; } catch { return false; } diff --git a/apps/roam/src/components/canvas/uiOverrides.tsx b/apps/roam/src/components/canvas/uiOverrides.tsx index cc0dee602..77c68118a 100644 --- a/apps/roam/src/components/canvas/uiOverrides.tsx +++ b/apps/roam/src/components/canvas/uiOverrides.tsx @@ -60,6 +60,7 @@ import { CanvasSyncMode } from "./canvasSyncMode"; import { getPersonalSetting } from "~/components/settings/utils/accessors"; import { PERSONAL_KEYS } from "~/components/settings/utils/settingKeys"; import posthog from "posthog-js"; +import { DISCOURSE_NODE_SHAPE_TYPE } from "./DiscourseNodeUtil"; const SyncModeMenuSwitchItem = ({ checked, @@ -182,7 +183,7 @@ export const getOnSelectForShape = ({ }); editor.createShapes([ { - type: nodeType, + type: DISCOURSE_NODE_SHAPE_TYPE, id: createShapeId(), props: { uid, @@ -192,6 +193,7 @@ export const getOnSelectForShape = ({ imageUrl: nodeImageUrl, fontFamily: "sans", size: "s", + nodeTypeId: nodeType, }, x, y, diff --git a/apps/roam/src/components/canvas/useCanvasStoreAdapterArgs.ts b/apps/roam/src/components/canvas/useCanvasStoreAdapterArgs.ts index 4791d6f1d..045e29568 100644 --- a/apps/roam/src/components/canvas/useCanvasStoreAdapterArgs.ts +++ b/apps/roam/src/components/canvas/useCanvasStoreAdapterArgs.ts @@ -5,7 +5,7 @@ import { TLAnyShapeUtilConstructor, } from "tldraw"; import { DiscourseNode } from "~/utils/getDiscourseNodes"; -import { createNodeShapeUtils } from "./DiscourseNodeUtil"; +import { DiscourseNodeUtil } from "./DiscourseNodeUtil"; import { createAllReferencedNodeUtils, createAllRelationShapeUtils, @@ -56,7 +56,6 @@ const getUtilTypes = ({ }; const createShapeUtils = ({ - allNodes, allRelationIds, allAddReferencedNodeByAction, }: { @@ -65,7 +64,7 @@ const createShapeUtils = ({ allAddReferencedNodeByAction: AddReferencedNodeType; }): TLAnyShapeUtilConstructor[] => { return [ - ...createNodeShapeUtils(allNodes), + DiscourseNodeUtil, ...createAllRelationShapeUtils(allRelationIds), ...createAllReferencedNodeUtils(allAddReferencedNodeByAction), ]; diff --git a/apps/roam/src/utils/syncCanvasNodeTitlesOnLoad.ts b/apps/roam/src/utils/syncCanvasNodeTitlesOnLoad.ts index acd74df5d..de4f94f3a 100644 --- a/apps/roam/src/utils/syncCanvasNodeTitlesOnLoad.ts +++ b/apps/roam/src/utils/syncCanvasNodeTitlesOnLoad.ts @@ -1,5 +1,8 @@ import type { Editor } from "tldraw"; -import type { DiscourseNodeShape } from "~/components/canvas/DiscourseNodeUtil"; +import { + DISCOURSE_NODE_SHAPE_TYPE, + type DiscourseNodeShape, +} from "~/components/canvas/DiscourseNodeUtil"; /** * Query Roam for current :node/title or :block/string for each uid. @@ -64,12 +67,13 @@ export const syncCanvasNodeTitlesOnLoad = async ( const nodeTypeSet = new Set(nodeTypeIds); const relationIds = new Set(relationShapeTypeIds); const allRecords = editor.store.allRecords(); - const discourseNodeShapes = allRecords.filter( - (r) => - r.typeName === "shape" && - nodeTypeSet.has((r as DiscourseNodeShape).type) && - typeof (r as DiscourseNodeShape).props?.uid === "string", - ) as DiscourseNodeShape[]; + const discourseNodeShapes = allRecords.filter((r) => { + if (r.typeName !== "shape") return false; + if (r.type !== DISCOURSE_NODE_SHAPE_TYPE) return false; + const shape = r as DiscourseNodeShape; + if (!nodeTypeSet.has(shape.props.nodeTypeId)) return false; + return typeof shape.props?.uid === "string"; + }) as DiscourseNodeShape[]; const uids = [...new Set(discourseNodeShapes.map((s) => s.props.uid))]; if (uids.length === 0) return;