canvas-website/src/ui/overrides.tsx

376 lines
11 KiB
TypeScript

import { shapeIdValidator, TLUiOverrides } from "tldraw"
import {
cameraHistory,
copyLinkToCurrentView,
lockCameraToFrame,
revertCamera,
zoomToSelection,
} from "./cameraUtils"
import { saveToPdf } from "../utils/pdfUtils"
import { searchText } from "../utils/searchUtils"
import { EmbedShape, IEmbedShape } from "@/shapes/EmbedShapeUtil"
export const overrides: TLUiOverrides = {
tools(editor, tools) {
return {
...tools,
select: {
...tools.select,
onPointerDown: (info: any) => {
const shape = editor.getShapeAtPoint(info.point)
if (shape && editor.getSelectedShapeIds().includes(shape.id)) {
// If clicking on a selected shape, initiate drag behavior
editor.dispatch({
type: "pointer",
name: "pointer_down",
point: info.point,
button: info.button,
shiftKey: info.shiftKey,
altKey: info.altKey,
ctrlKey: info.ctrlKey,
metaKey: info.metaKey,
pointerId: info.pointerId,
target: "shape",
shape,
isPen: false,
accelKey: info.ctrlKey || info.metaKey,
})
return
}
// Otherwise, use default select tool behavior
;(tools.select as any).onPointerDown?.(info)
},
//TODO: Fix double click to zoom on selector tool later...
onDoubleClick: (info: any) => {
const shape = editor.getShapeAtPoint(info.point)
if (shape?.type === 'Embed') {
// Let the Embed shape handle its own double-click behavior
const util = editor.getShapeUtil(shape) as EmbedShape
util?.onDoubleClick?.(shape as IEmbedShape)
return
}
// Handle all pointer types (mouse, touch, pen)
const point = info.point || (info.touches && info.touches[0]) || info
// Zoom in at the clicked/touched point
editor.zoomIn(point, { animation: { duration: 200 } })
// Stop event propagation and prevent default handling
info.stopPropagation?.()
return false
},
},
VideoChat: {
id: "VideoChat",
icon: "video",
label: "Video Chat",
kbd: "alt+v",
readonlyOk: true,
type: "VideoChat",
onSelect: () => editor.setCurrentTool("VideoChat"),
},
ChatBox: {
id: "ChatBox",
icon: "chat",
label: "Chat",
kbd: "alt+c",
readonlyOk: true,
type: "ChatBox",
onSelect: () => editor.setCurrentTool("ChatBox"),
},
Embed: {
id: "Embed",
icon: "embed",
label: "Embed",
kbd: "alt+e",
readonlyOk: true,
type: "Embed",
onSelect: () => editor.setCurrentTool("Embed"),
},
SlideShape: {
id: "Slide",
icon: "slides",
label: "Slide",
kbd: "alt+s",
type: "Slide",
readonlyOk: true,
onSelect: () => {
console.log('SlideShape tool selected from menu')
console.log('Current tool before:', editor.getCurrentToolId())
editor.setCurrentTool("Slide")
console.log('Current tool after:', editor.getCurrentToolId())
},
},
Markdown: {
id: "Markdown",
icon: "markdown",
label: "Markdown",
kbd: "alt+m",
readonlyOk: true,
type: "Markdown",
onSelect: () => editor.setCurrentTool("Markdown"),
},
MycrozineTemplate: {
id: "MycrozineTemplate",
icon: "rectangle",
label: "Mycrozine Template",
type: "MycrozineTemplate",
kbd: "m",
readonlyOk: true,
onSelect: () => editor.setCurrentTool("MycrozineTemplate"),
},
hand: {
...tools.hand,
onDoubleClick: (info: any) => {
editor.zoomIn(info.point, { animation: { duration: 200 } })
},
},
}
},
actions(editor, actions) {
return {
...actions,
zoomToSelection: {
id: "zoom-to-selection",
label: "Zoom to Selection",
kbd: "z",
onSelect: () => {
if (editor.getSelectedShapeIds().length > 0) {
zoomToSelection(editor)
}
},
readonlyOk: true,
},
copyLinkToCurrentView: {
id: "copy-link-to-current-view",
label: "Copy Link to Current View",
kbd: "alt+c",
onSelect: () => {
copyLinkToCurrentView(editor)
},
readonlyOk: true,
},
revertCamera: {
id: "revert-camera",
label: "Revert Camera",
kbd: "alt+b",
onSelect: () => {
if (cameraHistory.length > 0) {
revertCamera(editor)
}
},
readonlyOk: true,
},
lockToFrame: {
id: "lock-to-frame",
label: "Lock to Frame",
kbd: "shift+l",
onSelect: () => lockCameraToFrame(editor),
},
saveToPdf: {
id: "save-to-pdf",
label: "Save Selection as PDF",
kbd: "alt+p",
onSelect: () => {
if (editor.getSelectedShapeIds().length > 0) {
saveToPdf(editor)
}
},
readonlyOk: true,
},
moveSelectedLeft: {
id: "move-selected-left",
label: "Move Left",
kbd: "ArrowLeft",
onSelect: () => {
const selectedShapes = editor.getSelectedShapes()
if (selectedShapes.length > 0) {
selectedShapes.forEach((shape) => {
editor.updateShape({
id: shape.id,
type: shape.type,
x: shape.x - 50,
y: shape.y,
})
})
}
},
},
moveSelectedRight: {
id: "move-selected-right",
label: "Move Right",
kbd: "ArrowRight",
onSelect: () => {
const selectedShapes = editor.getSelectedShapes()
if (selectedShapes.length > 0) {
selectedShapes.forEach((shape) => {
editor.updateShape({
id: shape.id,
type: shape.type,
x: shape.x + 50,
y: shape.y,
})
})
}
},
},
moveSelectedUp: {
id: "move-selected-up",
label: "Move Up",
kbd: "ArrowUp",
onSelect: () => {
const selectedShapes = editor.getSelectedShapes()
if (selectedShapes.length > 0) {
selectedShapes.forEach((shape) => {
editor.updateShape({
id: shape.id,
type: shape.type,
x: shape.x,
y: shape.y - 50,
})
})
}
},
},
moveSelectedDown: {
id: "move-selected-down",
label: "Move Down",
kbd: "ArrowDown",
onSelect: () => {
const selectedShapes = editor.getSelectedShapes()
if (selectedShapes.length > 0) {
selectedShapes.forEach((shape) => {
editor.updateShape({
id: shape.id,
type: shape.type,
x: shape.x,
y: shape.y + 50,
})
})
}
},
},
// TODO: FIX THIS
resizeSelectedUp: {
id: "resize-selected-up",
label: "Resize Up",
kbd: "ctrl+ArrowUp",
onSelect: () => {
const selectedShapes = editor.getSelectedShapes()
if (selectedShapes.length > 0) {
selectedShapes.forEach((shape) => {
const bounds = editor.getShapeGeometry(shape).bounds
editor.updateShape({
id: shape.id,
type: shape.type,
//y: shape.y - 50,
props: {
...shape.props,
h: bounds.height + 50,
},
})
})
}
},
},
resizeSelectedDown: {
id: "resize-selected-down",
label: "Resize Down",
kbd: "ctrl+ArrowDown",
onSelect: () => {
const selectedShapes = editor.getSelectedShapes()
if (selectedShapes.length > 0) {
selectedShapes.forEach((shape) => {
const bounds = editor.getShapeGeometry(shape).bounds
editor.updateShape({
id: shape.id,
type: shape.type,
props: {
...shape.props,
h: bounds.height + 50,
},
})
})
}
},
},
resizeSelectedLeft: {
id: "resize-selected-left",
label: "Resize Left",
kbd: "ctrl+ArrowLeft",
onSelect: () => {
const selectedShapes = editor.getSelectedShapes()
if (selectedShapes.length > 0) {
selectedShapes.forEach((shape) => {
const bounds = editor.getShapeGeometry(shape).bounds
editor.updateShape({
id: shape.id,
type: shape.type,
props: {
...shape.props,
w: bounds.width + 50,
},
})
})
}
},
},
resizeSelectedRight: {
id: "resize-selected-right",
label: "Resize Right",
kbd: "ctrl+ArrowRight",
onSelect: () => {
const selectedShapes = editor.getSelectedShapes()
if (selectedShapes.length > 0) {
selectedShapes.forEach((shape) => {
const bounds = editor.getShapeGeometry(shape).bounds
editor.updateShape({
id: shape.id,
type: shape.type,
props: {
...shape.props,
w: bounds.width + 50,
},
})
})
}
},
},
//TODO: MAKE THIS WORK, ADD USER PERMISSIONING TO JOIN BROADCAST?
broadcastView: {
id: "broadcast-view",
label: "Broadcast View",
kbd: "alt+b",
readonlyOk: true,
onSelect: () => {
const collaborators = editor.getCollaborators()
collaborators
.filter((user) => user.id !== editor.user.getId())
.forEach((user) => {
editor.startFollowingUser(user.id)
})
},
},
stopBroadcast: {
id: "stop-broadcast",
label: "Stop Broadcasting",
kbd: "alt+shift+b",
readonlyOk: true,
onSelect: () => {
editor.stopFollowingUser()
},
},
searchShapes: {
id: "search-shapes",
label: "Search Shapes",
kbd: "s",
readonlyOk: true,
onSelect: () => searchText(editor),
},
}
},
}