diff --git a/src/main/frontend/components/container.css b/src/main/frontend/components/container.css
index ccce10ffbcb..09b5c2c90cd 100644
--- a/src/main/frontend/components/container.css
+++ b/src/main/frontend/components/container.css
@@ -524,7 +524,7 @@ html[data-theme='dark'] {
}
&-btn {
- @apply fixed bottom-4 right-8;
+ @apply fixed bottom-4 right-4 sm:right-8;
> .inner {
@apply font-bold
diff --git a/src/main/frontend/components/whiteboard.css b/src/main/frontend/components/whiteboard.css
index e6f6184ab36..51427384b4f 100644
--- a/src/main/frontend/components/whiteboard.css
+++ b/src/main/frontend/components/whiteboard.css
@@ -205,7 +205,7 @@ input.tl-text-input {
.tl-action-bar {
left: 0.5rem;
- bottom: 0.5rem;
+ bottom: 0;
}
.tl-primary-tools {
diff --git a/src/resources/dicts/en.edn b/src/resources/dicts/en.edn
index 9a6eacf75e6..42d7421c198 100644
--- a/src/resources/dicts/en.edn
+++ b/src/resources/dicts/en.edn
@@ -465,6 +465,7 @@
:whiteboard/dashboard-card-edited "Edited "
:whiteboard/toggle-grid "Toggle grid"
:whiteboard/snap-to-grid "Snap to grid"
+ :whiteboard/toggle-pen-mode "Toggle pen mode"
:flashcards/modal-welcome-title "Time to create a card!"
:flashcards/modal-welcome-desc-1 "You can add \"#card\" to any block to turn it into a card or trigger \"/cloze\" to add some clozes."
:flashcards/modal-welcome-desc-2 "You can "
diff --git a/tldraw/apps/tldraw-logseq/src/components/ActionBar/ActionBar.tsx b/tldraw/apps/tldraw-logseq/src/components/ActionBar/ActionBar.tsx
index 430783b6a08..4183dc50e0a 100644
--- a/tldraw/apps/tldraw-logseq/src/components/ActionBar/ActionBar.tsx
+++ b/tldraw/apps/tldraw-logseq/src/components/ActionBar/ActionBar.tsx
@@ -41,10 +41,14 @@ export const ActionBar = observer(function ActionBar(): JSX.Element {
app.api.toggleSnapToGrid()
}, [app])
+ const togglePenMode = React.useCallback(() => {
+ app.api.togglePenMode()
+ }, [app])
+
return (
{!app.readOnly && (
-
+
@@ -54,7 +58,7 @@ export const ActionBar = observer(function ActionBar(): JSX.Element {
)}
-
+
@@ -65,7 +69,7 @@ export const ActionBar = observer(function ActionBar(): JSX.Element {
-
+
)}
+
+ {!app.readOnly && (
+
+ )}
)
})
diff --git a/tldraw/apps/tldraw-logseq/src/styles.css b/tldraw/apps/tldraw-logseq/src/styles.css
index 61553f6f47d..0f91cc68a49 100644
--- a/tldraw/apps/tldraw-logseq/src/styles.css
+++ b/tldraw/apps/tldraw-logseq/src/styles.css
@@ -166,7 +166,7 @@ html[data-theme='light'] {
}
.tl-action-bar {
- @apply absolute bottom-0 flex items-center border-0 left-10 bottom-10;
+ @apply absolute flex items-center border-0 left-10 bottom-8 flex-wrap-reverse pr-12;
z-index: 100000;
user-select: none;
diff --git a/tldraw/packages/core/src/lib/TLApi/TLApi.ts b/tldraw/packages/core/src/lib/TLApi/TLApi.ts
index 3b6989fb05c..ab8bd2e4f9a 100644
--- a/tldraw/packages/core/src/lib/TLApi/TLApi.ts
+++ b/tldraw/packages/core/src/lib/TLApi/TLApi.ts
@@ -178,6 +178,13 @@ export class TLApi
{
+ const { settings } = this.app
+ settings.update({ penMode: !settings.penMode })
+ return this
+ }
+
setColor = (color: string): this => {
const { settings } = this.app
diff --git a/tldraw/packages/core/src/lib/TLSettings.ts b/tldraw/packages/core/src/lib/TLSettings.ts
index c6383ef6f8f..2757a4d5a15 100644
--- a/tldraw/packages/core/src/lib/TLSettings.ts
+++ b/tldraw/packages/core/src/lib/TLSettings.ts
@@ -4,6 +4,7 @@ import { observable, makeObservable, action } from 'mobx'
export interface TLSettingsProps {
mode: 'light' | 'dark'
showGrid: boolean
+ penMode: boolean
snapToGrid: boolean
color: string
scaleLevel: string
@@ -17,6 +18,7 @@ export class TLSettings implements TLSettingsProps {
@observable mode: 'dark' | 'light' = 'light'
@observable showGrid = true
@observable snapToGrid = true
+ @observable penMode = false
@observable scaleLevel = 'md'
@observable color = ''
diff --git a/tldraw/packages/react/src/components/AppCanvas.tsx b/tldraw/packages/react/src/components/AppCanvas.tsx
index 91940c94084..76a43027130 100644
--- a/tldraw/packages/react/src/components/AppCanvas.tsx
+++ b/tldraw/packages/react/src/components/AppCanvas.tsx
@@ -27,6 +27,7 @@ export const AppCanvas = observer(function InnerApp(
shapes={app.shapes} // TODO: use shapes in viewport later?
assets={app.assets}
showGrid={app.settings.showGrid}
+ penMode={app.settings.penMode}
showSelection={app.showSelection}
showSelectionRotation={app.showSelectionRotation}
showResizeHandles={app.showResizeHandles}
diff --git a/tldraw/packages/react/src/components/Canvas/Canvas.tsx b/tldraw/packages/react/src/components/Canvas/Canvas.tsx
index 5969563c624..0234b45dadf 100644
--- a/tldraw/packages/react/src/components/Canvas/Canvas.tsx
+++ b/tldraw/packages/react/src/components/Canvas/Canvas.tsx
@@ -58,6 +58,7 @@ export interface TLCanvasProps {
cursorRotation: number
selectionRotation: number
onEditingEnd: () => void
+ penMode: boolean
showGrid: boolean
showSelection: boolean
showHandles: boolean
diff --git a/tldraw/packages/react/src/hooks/useCanvasEvents.ts b/tldraw/packages/react/src/hooks/useCanvasEvents.ts
index 4fef6ee0ae4..5c596630883 100644
--- a/tldraw/packages/react/src/hooks/useCanvasEvents.ts
+++ b/tldraw/packages/react/src/hooks/useCanvasEvents.ts
@@ -13,11 +13,19 @@ export function useCanvasEvents() {
const events = React.useMemo(() => {
const onPointerMove: TLReactCustomEvents['pointer'] = e => {
+ if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+ return
+ }
+
const { order = 0 } = e
callbacks.onPointerMove?.({ type: TLTargetType.Canvas, order }, e)
}
const onPointerDown: TLReactCustomEvents['pointer'] = e => {
+ if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+ return
+ }
+
const { order = 0 } = e
if (!order) e.currentTarget?.setPointerCapture(e.pointerId)
@@ -41,17 +49,29 @@ export function useCanvasEvents() {
}
const onPointerUp: TLReactCustomEvents['pointer'] = e => {
+ if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+ return
+ }
+
const { order = 0 } = e
if (!order) e.currentTarget?.releasePointerCapture(e.pointerId)
callbacks.onPointerUp?.({ type: TLTargetType.Canvas, order }, e)
}
const onPointerEnter: TLReactCustomEvents['pointer'] = e => {
+ if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+ return
+ }
+
const { order = 0 } = e
callbacks.onPointerEnter?.({ type: TLTargetType.Canvas, order }, e)
}
const onPointerLeave: TLReactCustomEvents['pointer'] = e => {
+ if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+ return
+ }
+
const { order = 0 } = e
callbacks.onPointerLeave?.({ type: TLTargetType.Canvas, order }, e)
}
diff --git a/tldraw/packages/react/src/hooks/useShapeEvents.ts b/tldraw/packages/react/src/hooks/useShapeEvents.ts
index 99e38e839d2..81aaaa450d0 100644
--- a/tldraw/packages/react/src/hooks/useShapeEvents.ts
+++ b/tldraw/packages/react/src/hooks/useShapeEvents.ts
@@ -1,23 +1,33 @@
import * as React from 'react'
import { TLTargetType } from '@tldraw/core'
+import { useApp } from './useApp'
import { useRendererContext } from '.'
import { DOUBLE_CLICK_DURATION } from '../constants'
import type { TLReactShape } from '../lib'
import type { TLReactCustomEvents } from '../types'
export function useShapeEvents(shape: S) {
+ const app = useApp()
const { inputs, callbacks } = useRendererContext()
const rDoubleClickTimer = React.useRef(-1)
const events = React.useMemo(() => {
const onPointerMove: TLReactCustomEvents['pointer'] = e => {
+ if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+ return
+ }
+
const { order = 0 } = e
callbacks.onPointerMove?.({ type: TLTargetType.Shape, shape, order }, e)
e.order = order + 1
}
const onPointerDown: TLReactCustomEvents['pointer'] = e => {
+ if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+ return
+ }
+
const { order = 0 } = e
if (!order) e.currentTarget?.setPointerCapture(e.pointerId)
callbacks.onPointerDown?.({ type: TLTargetType.Shape, shape, order }, e)
@@ -25,6 +35,10 @@ export function useShapeEvents(shape: S) {
}
const onPointerUp: TLReactCustomEvents['pointer'] = e => {
+ if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+ return
+ }
+
const { order = 0 } = e
if (!order) e.currentTarget?.releasePointerCapture(e.pointerId)
callbacks.onPointerUp?.({ type: TLTargetType.Shape, shape, order }, e)
@@ -42,12 +56,20 @@ export function useShapeEvents(shape: S) {
}
const onPointerEnter: TLReactCustomEvents['pointer'] = e => {
+ if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+ return
+ }
+
const { order = 0 } = e
callbacks.onPointerEnter?.({ type: TLTargetType.Shape, shape, order }, e)
e.order = order + 1
}
const onPointerLeave: TLReactCustomEvents['pointer'] = e => {
+ if (app.settings.penMode && (e.pointerType !== 'pen' || !e.isPrimary)) {
+ return
+ }
+
const { order = 0 } = e
callbacks.onPointerLeave?.({ type: TLTargetType.Shape, shape, order }, e)
e.order = order + 1