diff --git a/apps/builder/assets/icons.tsx b/apps/builder/assets/icons.tsx
index 26fb70fef..755035744 100644
--- a/apps/builder/assets/icons.tsx
+++ b/apps/builder/assets/icons.tsx
@@ -403,3 +403,35 @@ export const TemplateIcon = (props: IconProps) => (
)
+
+export const MinusIcon = (props: IconProps) => (
+
+
+
+)
+
+export const LaptopIcon = (props: IconProps) => (
+
+
+
+
+)
+
+export const MouseIcon = (props: IconProps) => (
+
+
+
+)
diff --git a/apps/builder/components/editor/BoardMenuButton.tsx b/apps/builder/components/editor/BoardMenuButton.tsx
index 484b25010..f1d4b03f6 100644
--- a/apps/builder/components/editor/BoardMenuButton.tsx
+++ b/apps/builder/components/editor/BoardMenuButton.tsx
@@ -5,16 +5,19 @@ import {
MenuButtonProps,
MenuItem,
MenuList,
+ useDisclosure,
} from '@chakra-ui/react'
import assert from 'assert'
-import { DownloadIcon, MoreVerticalIcon } from 'assets/icons'
+import { DownloadIcon, MoreVerticalIcon, SettingsIcon } from 'assets/icons'
import { useTypebot } from 'contexts/TypebotContext'
import React, { useState } from 'react'
import { parseDefaultPublicId } from 'services/typebots'
+import { EditorSettingsModal } from './EditorSettingsModal'
export const BoardMenuButton = (props: MenuButtonProps) => {
const { typebot } = useTypebot()
const [isDownloading, setIsDownloading] = useState(false)
+ const { isOpen, onOpen, onClose } = useDisclosure()
const downloadFlow = () => {
assert(typebot)
@@ -36,18 +39,22 @@ export const BoardMenuButton = (props: MenuButtonProps) => {
)
}
diff --git a/apps/builder/components/editor/EditorSettingsModal.tsx b/apps/builder/components/editor/EditorSettingsModal.tsx
new file mode 100644
index 000000000..38bdca684
--- /dev/null
+++ b/apps/builder/components/editor/EditorSettingsModal.tsx
@@ -0,0 +1,97 @@
+import {
+ Heading,
+ Modal,
+ ModalBody,
+ ModalCloseButton,
+ ModalContent,
+ ModalOverlay,
+ Stack,
+ Text,
+ Radio,
+ VStack,
+ RadioGroup,
+ HStack,
+} from '@chakra-ui/react'
+import { LaptopIcon, MouseIcon } from 'assets/icons'
+import { useUser } from 'contexts/UserContext'
+import { GraphNavigation } from 'db'
+import React, { useEffect, useState } from 'react'
+
+type Props = {
+ isOpen: boolean
+ onClose: () => void
+}
+
+export const EditorSettingsModal = ({ isOpen, onClose }: Props) => {
+ return (
+
+
+
+
+
+
+
+
+
+ )
+}
+
+const EditorSettings = () => {
+ const { user, saveUser } = useUser()
+ const [value, setValue] = useState(
+ user?.graphNavigation ?? GraphNavigation.TRACKPAD
+ )
+
+ useEffect(() => {
+ if (user?.graphNavigation === value) return
+ saveUser({ graphNavigation: value as GraphNavigation }).then()
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [value])
+
+ const options = [
+ {
+ value: GraphNavigation.MOUSE,
+ label: 'Mouse',
+ description:
+ 'Move by dragging the board and zoom in/out using the scroll wheel',
+ icon: ,
+ },
+ {
+ value: GraphNavigation.TRACKPAD,
+ label: 'Trackpad',
+ description: 'Move the board using 2 fingers and zoom in/out by pinching',
+ icon: ,
+ },
+ ]
+
+ return (
+
+ Navigation
+
+
+ {options.map((option) => (
+
+ {option.icon}
+
+ {option.label}
+ {option.description}
+
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/apps/builder/components/shared/Graph/Edges/DrawingEdge.tsx b/apps/builder/components/shared/Graph/Edges/DrawingEdge.tsx
index a4c64151f..df581c41d 100644
--- a/apps/builder/components/shared/Graph/Edges/DrawingEdge.tsx
+++ b/apps/builder/components/shared/Graph/Edges/DrawingEdge.tsx
@@ -29,21 +29,23 @@ export const DrawingEdge = () => {
const sourceTop = useMemo(() => {
if (!connectingIds) return 0
- return getEndpointTopOffset(
- sourceEndpoints,
- graphPosition.y,
- connectingIds.source.itemId ?? connectingIds.source.stepId
- )
+ return getEndpointTopOffset({
+ endpoints: sourceEndpoints,
+ graphOffsetY: graphPosition.y,
+ endpointId: connectingIds.source.itemId ?? connectingIds.source.stepId,
+ graphScale: graphPosition.scale,
+ })
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [connectingIds, sourceEndpoints])
const targetTop = useMemo(() => {
if (!connectingIds) return 0
- return getEndpointTopOffset(
- targetEndpoints,
- graphPosition.y,
- connectingIds.target?.stepId
- )
+ return getEndpointTopOffset({
+ endpoints: targetEndpoints,
+ graphOffsetY: graphPosition.y,
+ endpointId: connectingIds.target?.stepId,
+ graphScale: graphPosition.scale,
+ })
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [connectingIds, targetEndpoints])
@@ -56,6 +58,7 @@ export const DrawingEdge = () => {
targetBlockCoordinates,
sourceTop,
targetTop,
+ graphScale: graphPosition.scale,
})
: computeEdgePathToMouse({
sourceBlockCoordinates,
@@ -68,13 +71,15 @@ export const DrawingEdge = () => {
targetBlockCoordinates,
targetTop,
mousePosition,
+ graphPosition.scale,
])
const handleMouseMove = (e: MouseEvent) => {
- setMousePosition({
- x: e.clientX - graphPosition.x,
- y: e.clientY - graphPosition.y,
- })
+ const coordinates = {
+ x: (e.clientX - graphPosition.x) / graphPosition.scale,
+ y: (e.clientY - graphPosition.y) / graphPosition.scale,
+ }
+ setMousePosition(coordinates)
}
useEventListener('mousemove', handleMouseMove)
useEventListener('mouseup', () => {
diff --git a/apps/builder/components/shared/Graph/Edges/DropOffEdge.tsx b/apps/builder/components/shared/Graph/Edges/DropOffEdge.tsx
index abde22494..0d1fefe13 100644
--- a/apps/builder/components/shared/Graph/Edges/DropOffEdge.tsx
+++ b/apps/builder/components/shared/Graph/Edges/DropOffEdge.tsx
@@ -58,11 +58,12 @@ export const DropOffEdge = ({
const block = publishedTypebot?.blocks.find(byId(blockId))
const sourceTop = useMemo(
() =>
- getEndpointTopOffset(
- sourceEndpoints,
- graphPosition.y,
- block?.steps[block.steps.length - 1].id
- ),
+ getEndpointTopOffset({
+ endpoints: sourceEndpoints,
+ graphOffsetY: graphPosition.y,
+ endpointId: block?.steps[block.steps.length - 1].id,
+ graphScale: graphPosition.scale,
+ }),
// eslint-disable-next-line react-hooks/exhaustive-deps
[block?.steps, sourceEndpoints, blocksCoordinates]
)
diff --git a/apps/builder/components/shared/Graph/Edges/Edge.tsx b/apps/builder/components/shared/Graph/Edges/Edge.tsx
index 5e88c7b2b..7b909ce64 100644
--- a/apps/builder/components/shared/Graph/Edges/Edge.tsx
+++ b/apps/builder/components/shared/Graph/Edges/Edge.tsx
@@ -44,11 +44,12 @@ export const Edge = ({ edge }: { edge: EdgeProps }) => {
const sourceTop = useMemo(
() =>
- getEndpointTopOffset(
- sourceEndpoints,
- graphPosition.y,
- getSourceEndpointId(edge)
- ),
+ getEndpointTopOffset({
+ endpoints: sourceEndpoints,
+ graphOffsetY: graphPosition.y,
+ endpointId: getSourceEndpointId(edge),
+ graphScale: graphPosition.scale,
+ }),
// eslint-disable-next-line react-hooks/exhaustive-deps
[sourceBlockCoordinates?.y, edge, sourceEndpoints, refreshEdge]
)
@@ -58,17 +59,28 @@ export const Edge = ({ edge }: { edge: EdgeProps }) => {
}, [])
const [targetTop, setTargetTop] = useState(
- getEndpointTopOffset(targetEndpoints, graphPosition.y, edge?.to.stepId)
+ getEndpointTopOffset({
+ endpoints: targetEndpoints,
+ graphOffsetY: graphPosition.y,
+ endpointId: edge?.to.stepId,
+ graphScale: graphPosition.scale,
+ })
)
useLayoutEffect(() => {
setTargetTop(
- getEndpointTopOffset(targetEndpoints, graphPosition.y, edge?.to.stepId)
+ getEndpointTopOffset({
+ endpoints: targetEndpoints,
+ graphOffsetY: graphPosition.y,
+ endpointId: edge?.to.stepId,
+ graphScale: graphPosition.scale,
+ })
)
}, [
targetBlockCoordinates?.y,
targetEndpoints,
graphPosition.y,
edge?.to.stepId,
+ graphPosition.scale,
])
const path = useMemo(() => {
@@ -79,6 +91,7 @@ export const Edge = ({ edge }: { edge: EdgeProps }) => {
targetBlockCoordinates,
sourceTop,
targetTop,
+ graphScale: graphPosition.scale,
})
return computeEdgePath(anchorsPosition)
// eslint-disable-next-line react-hooks/exhaustive-deps
diff --git a/apps/builder/components/shared/Graph/Graph.tsx b/apps/builder/components/shared/Graph/Graph.tsx
index b4b62c8d5..daffa40d6 100644
--- a/apps/builder/components/shared/Graph/Graph.tsx
+++ b/apps/builder/components/shared/Graph/Graph.tsx
@@ -2,20 +2,26 @@ import { Flex, FlexProps, useEventListener } from '@chakra-ui/react'
import React, { useRef, useMemo, useEffect, useState } from 'react'
import {
blockWidth,
+ Coordinates,
graphPositionDefaultValue,
useGraph,
} from 'contexts/GraphContext'
import { useStepDnd } from 'contexts/GraphDndContext'
import { useTypebot } from 'contexts/TypebotContext/TypebotContext'
-import { headerHeight } from 'components/shared/TypebotHeader/TypebotHeader'
import { DraggableStepType, PublicTypebot, Typebot } from 'models'
import { AnswersCount } from 'services/analytics'
import { useDebounce } from 'use-debounce'
import { DraggableCore, DraggableData, DraggableEvent } from 'react-draggable'
import GraphContent from './GraphContent'
import cuid from 'cuid'
+import { headerHeight } from '../TypebotHeader'
+import { useUser } from 'contexts/UserContext'
+import { GraphNavigation } from 'db'
+import { ZoomButtons } from './ZoomButtons'
-declare const window: { chrome: unknown | undefined }
+const maxScale = 1.5
+const minScale = 0.1
+const zoomButtonsScaleStep = 0.2
export const Graph = ({
typebot,
@@ -51,6 +57,7 @@ export const Graph = ({
`translate(${graphPosition.x}px, ${graphPosition.y}px) scale(${graphPosition.scale})`,
[graphPosition]
)
+ const { user } = useUser()
useEffect(() => {
editorContainerRef.current = document.getElementById(
@@ -65,7 +72,7 @@ export const Graph = ({
setGlobalGraphPosition({
x: left + debouncedGraphPosition.x,
y: top + debouncedGraphPosition.y,
- scale: 1,
+ scale: debouncedGraphPosition.scale,
})
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [debouncedGraphPosition])
@@ -73,22 +80,26 @@ export const Graph = ({
const handleMouseWheel = (e: WheelEvent) => {
e.preventDefault()
const isPinchingTrackpad = e.ctrlKey
- if (isPinchingTrackpad) return
- setGraphPosition({
- ...graphPosition,
- x: graphPosition.x - e.deltaX / (window.chrome ? 2 : 1),
- y: graphPosition.y - e.deltaY / (window.chrome ? 2 : 1),
- })
+ user?.graphNavigation === GraphNavigation.MOUSE
+ ? zoom(-e.deltaY * 0.01, { x: e.clientX, y: e.clientY })
+ : isPinchingTrackpad
+ ? zoom(-e.deltaY * 0.01, { x: e.clientX, y: e.clientY })
+ : setGraphPosition({
+ ...graphPosition,
+ x: graphPosition.x - e.deltaX,
+ y: graphPosition.y - e.deltaY,
+ })
}
const handleMouseUp = (e: MouseEvent) => {
if (!typebot) return
if (draggedItem) setDraggedItem(undefined)
if (!draggedStep && !draggedStepType) return
- const coordinates = {
- x: e.clientX - graphPosition.x - blockWidth / 3,
- y: e.clientY - graphPosition.y - 20 - headerHeight,
- }
+
+ const coordinates = projectMouse(
+ { x: e.clientX, y: e.clientY },
+ graphPosition
+ )
const id = cuid()
updateBlockCoordinates(id, coordinates)
createBlock({
@@ -121,15 +132,40 @@ export const Graph = ({
const onDrag = (_: DraggableEvent, draggableData: DraggableData) => {
const { deltaX, deltaY } = draggableData
setGraphPosition({
+ ...graphPosition,
x: graphPosition.x + deltaX,
y: graphPosition.y + deltaY,
- scale: 1,
+ })
+ }
+
+ const zoom = (delta = zoomButtonsScaleStep, mousePosition?: Coordinates) => {
+ const { x: mouseX, y } = mousePosition ?? { x: 0, y: 0 }
+ const mouseY = y - headerHeight
+ let scale = graphPosition.scale + delta
+ if (
+ (scale >= maxScale && graphPosition.scale === maxScale) ||
+ (scale <= minScale && graphPosition.scale === minScale)
+ )
+ return
+ scale = scale >= maxScale ? maxScale : scale <= minScale ? minScale : scale
+
+ const xs = (mouseX - graphPosition.x) / graphPosition.scale
+ const ys = (mouseY - graphPosition.y) / graphPosition.scale
+ setGraphPosition({
+ ...graphPosition,
+ x: mouseX - xs * scale,
+ y: mouseY - ys * scale,
+ scale,
})
}
return (
+ zoom(zoomButtonsScaleStep)}
+ onZoomOut={() => zoom(-zoomButtonsScaleStep)}
+ />
)
}
+
+const projectMouse = (
+ mouseCoordinates: Coordinates,
+ graphPosition: Coordinates & { scale: number }
+) => {
+ return {
+ x:
+ (mouseCoordinates.x -
+ graphPosition.x -
+ blockWidth / (3 / graphPosition.scale)) /
+ graphPosition.scale,
+ y:
+ (mouseCoordinates.y -
+ graphPosition.y -
+ (headerHeight + 20 * graphPosition.scale)) /
+ graphPosition.scale,
+ }
+}
diff --git a/apps/builder/components/shared/Graph/Nodes/BlockNode/BlockNode.tsx b/apps/builder/components/shared/Graph/Nodes/BlockNode/BlockNode.tsx
index b6b810530..41d31c66a 100644
--- a/apps/builder/components/shared/Graph/Nodes/BlockNode/BlockNode.tsx
+++ b/apps/builder/components/shared/Graph/Nodes/BlockNode/BlockNode.tsx
@@ -32,6 +32,7 @@ export const BlockNode = ({ block, blockIndex }: Props) => {
isReadOnly,
focusedBlockId,
setFocusedBlockId,
+ graphPosition,
} = useGraph()
const { typebot, updateBlock } = useTypebot()
const { setMouseOverBlock, mouseOverBlock } = useStepDnd()
@@ -86,11 +87,11 @@ export const BlockNode = ({ block, blockIndex }: Props) => {
if (connectingIds) setConnectingIds({ ...connectingIds, target: undefined })
}
- const onDrag = (event: DraggableEvent, draggableData: DraggableData) => {
+ const onDrag = (_: DraggableEvent, draggableData: DraggableData) => {
const { deltaX, deltaY } = draggableData
updateBlockCoordinates(block.id, {
- x: blockCoordinates.x + deltaX,
- y: blockCoordinates.y + deltaY,
+ x: blockCoordinates.x + deltaX / graphPosition.scale,
+ y: blockCoordinates.y + deltaY / graphPosition.scale,
})
}
diff --git a/apps/builder/components/shared/Graph/Nodes/ItemNode/ItemNodesList.tsx b/apps/builder/components/shared/Graph/Nodes/ItemNode/ItemNodesList.tsx
index 9c4be8c12..921547392 100644
--- a/apps/builder/components/shared/Graph/Nodes/ItemNode/ItemNodesList.tsx
+++ b/apps/builder/components/shared/Graph/Nodes/ItemNode/ItemNodesList.tsx
@@ -3,7 +3,7 @@ import {
computeNearestPlaceholderIndex,
useStepDnd,
} from 'contexts/GraphDndContext'
-import { Coordinates } from 'contexts/GraphContext'
+import { Coordinates, useGraph } from 'contexts/GraphContext'
import { useTypebot } from 'contexts/TypebotContext'
import { ButtonItem, StepIndices, StepWithItems } from 'models'
import React, { useEffect, useRef, useState } from 'react'
@@ -25,6 +25,7 @@ export const ItemNodesList = ({
const { typebot, createItem, detachItemFromStep } = useTypebot()
const { draggedItem, setDraggedItem, mouseOverBlock } = useStepDnd()
const placeholderRefs = useRef([])
+ const { graphPosition } = useGraph()
const blockId = typebot?.blocks[blockIndex].id
const isDraggingOnCurrentBlock =
(draggedItem && mouseOverBlock?.id === blockId) ?? false
@@ -182,8 +183,9 @@ export const ItemNodesList = ({
top="0"
left="0"
style={{
- transform: `translate(${position.x}px, ${position.y}px) rotate(-2deg)`,
+ transform: `translate(${position.x}px, ${position.y}px) rotate(-2deg) scale(${graphPosition.scale})`,
}}
+ transformOrigin="0 0 0"
/>
)}
diff --git a/apps/builder/components/shared/Graph/Nodes/StepNode/StepNodesList.tsx b/apps/builder/components/shared/Graph/Nodes/StepNode/StepNodesList.tsx
index 35641f3ce..1b245f669 100644
--- a/apps/builder/components/shared/Graph/Nodes/StepNode/StepNodesList.tsx
+++ b/apps/builder/components/shared/Graph/Nodes/StepNode/StepNodesList.tsx
@@ -32,7 +32,7 @@ export const StepNodesList = ({
setDraggedStepType,
} = useStepDnd()
const { typebot, createStep, detachStepFromBlock } = useTypebot()
- const { isReadOnly } = useGraph()
+ const { isReadOnly, graphPosition } = useGraph()
const [expandedPlaceholderIndex, setExpandedPlaceholderIndex] = useState<
number | undefined
>()
@@ -166,8 +166,9 @@ export const StepNodesList = ({
top="0"
left="0"
style={{
- transform: `translate(${position.x}px, ${position.y}px) rotate(-2deg)`,
+ transform: `translate(${position.x}px, ${position.y}px) rotate(-2deg) scale(${graphPosition.scale})`,
}}
+ transformOrigin="0 0 0"
/>
)}
diff --git a/apps/builder/components/shared/Graph/ZoomButtons.tsx b/apps/builder/components/shared/Graph/ZoomButtons.tsx
new file mode 100644
index 000000000..365b21bcf
--- /dev/null
+++ b/apps/builder/components/shared/Graph/ZoomButtons.tsx
@@ -0,0 +1,37 @@
+import { Stack, IconButton } from '@chakra-ui/react'
+import { PlusIcon, MinusIcon } from 'assets/icons'
+import { headerHeight } from '../TypebotHeader'
+
+type Props = {
+ onZoomIn: () => void
+ onZoomOut: () => void
+}
+export const ZoomButtons = ({ onZoomIn, onZoomOut }: Props) => (
+
+ }
+ aria-label={'Zoom out'}
+ size="sm"
+ onClick={onZoomIn}
+ bgColor="white"
+ borderBottomRadius={0}
+ />
+ }
+ aria-label={'Zoom out'}
+ size="sm"
+ onClick={onZoomOut}
+ bgColor="white"
+ borderTopRadius={0}
+ />
+
+)
diff --git a/apps/builder/contexts/GraphDndContext.tsx b/apps/builder/contexts/GraphDndContext.tsx
index 3b1158d1b..b12cd2d95 100644
--- a/apps/builder/contexts/GraphDndContext.tsx
+++ b/apps/builder/contexts/GraphDndContext.tsx
@@ -68,8 +68,10 @@ export const useDragDistance = ({
distanceTolerance?: number
isDisabled: boolean
}) => {
- const mouseDownPosition =
- useRef<{ absolute: Coordinates; relative: Coordinates }>()
+ const mouseDownPosition = useRef<{
+ absolute: Coordinates
+ relative: Coordinates
+ }>()
const handleMouseUp = () => {
if (mouseDownPosition) mouseDownPosition.current = undefined
diff --git a/apps/builder/contexts/UserContext.tsx b/apps/builder/contexts/UserContext.tsx
index afbbc3884..16f64462a 100644
--- a/apps/builder/contexts/UserContext.tsx
+++ b/apps/builder/contexts/UserContext.tsx
@@ -21,7 +21,7 @@ const userContext = createContext<{
hasUnsavedChanges: boolean
isOAuthProvider: boolean
updateUser: (newUser: Partial) => void
- saveUser: (newUser?: Partial) => void
+ saveUser: (newUser?: Partial) => Promise
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
//@ts-ignore
}>({})
diff --git a/apps/builder/services/graph.ts b/apps/builder/services/graph.ts
index 43714443c..264b4f290 100644
--- a/apps/builder/services/graph.ts
+++ b/apps/builder/services/graph.ts
@@ -129,6 +129,7 @@ type GetAnchorsPositionParams = {
targetBlockCoordinates: Coordinates
sourceTop: number
targetTop?: number
+ graphScale: number
}
export const getAnchorsPosition = ({
sourceBlockCoordinates,
@@ -236,12 +237,14 @@ export const computeConnectingEdgePath = ({
targetBlockCoordinates,
sourceTop,
targetTop,
+ graphScale,
}: GetAnchorsPositionParams) => {
const anchorsPosition = getAnchorsPosition({
sourceBlockCoordinates,
targetBlockCoordinates,
sourceTop,
targetTop,
+ graphScale,
})
return computeEdgePath(anchorsPosition)
}
@@ -258,8 +261,8 @@ export const computeEdgePathToMouse = ({
const sourcePosition = {
x:
mousePosition.x - sourceBlockCoordinates.x > blockWidth / 2
- ? sourceBlockCoordinates.x + blockWidth - 40
- : sourceBlockCoordinates.x + 40,
+ ? sourceBlockCoordinates.x + blockWidth
+ : sourceBlockCoordinates.x,
y: sourceTop,
}
const sourceType =
@@ -277,19 +280,26 @@ export const computeEdgePathToMouse = ({
).path
}
-export const getEndpointTopOffset = (
- endpoints: IdMap,
- graphOffsetY: number,
+export const getEndpointTopOffset = ({
+ endpoints,
+ graphOffsetY,
+ endpointId,
+ graphScale,
+}: {
+ endpoints: IdMap
+ graphOffsetY: number
endpointId?: string
-): number | undefined => {
+ graphScale: number
+}): number | undefined => {
if (!endpointId) return
const endpointRef = endpoints[endpointId]?.ref
if (!endpointRef?.current) return
- const endpointHeight = 28
+ const endpointHeight = 28 * graphScale
return (
- endpointRef.current.getBoundingClientRect().top +
- endpointHeight / 2 -
- graphOffsetY
+ (endpointRef.current.getBoundingClientRect().y +
+ endpointHeight / 2 -
+ graphOffsetY) /
+ graphScale
)
}
diff --git a/packages/db/prisma/migrations/20220408192635_add_editor_navigation/migration.sql b/packages/db/prisma/migrations/20220408192635_add_editor_navigation/migration.sql
new file mode 100644
index 000000000..835070b3b
--- /dev/null
+++ b/packages/db/prisma/migrations/20220408192635_add_editor_navigation/migration.sql
@@ -0,0 +1,5 @@
+-- CreateEnum
+CREATE TYPE "GraphNavigation" AS ENUM ('MOUSE', 'TRACKPAD');
+
+-- AlterTable
+ALTER TABLE "User" ADD COLUMN "graphNavigation" "GraphNavigation";
diff --git a/packages/db/prisma/schema.prisma b/packages/db/prisma/schema.prisma
index 01afc9b6d..721b1010c 100644
--- a/packages/db/prisma/schema.prisma
+++ b/packages/db/prisma/schema.prisma
@@ -58,6 +58,12 @@ model User {
CollaboratorsOnTypebots CollaboratorsOnTypebots[]
company String?
onboardingCategories String[]
+ graphNavigation GraphNavigation?
+}
+
+enum GraphNavigation {
+ MOUSE
+ TRACKPAD
}
model CustomDomain {