From c2abfcd3e3e894f5850a444162a3508333913476 Mon Sep 17 00:00:00 2001 From: Jeff-Emmett Date: Tue, 28 Jan 2025 16:38:41 +0100 Subject: [PATCH] Clean up tool names --- src/routes/Board.tsx | 4 +- src/shapes/PromptShapeUtil.tsx | 295 +++++++++++++++++---------------- src/shapes/SlideShapeUtil.tsx | 226 +++++++++++++------------ src/tools/PromptShapeTool.ts | 4 +- src/tools/SlideShapeTool.ts | 2 +- src/ui/CustomContextMenu.tsx | 14 +- src/ui/CustomToolbar.tsx | 173 ++++++++++++++----- src/ui/components.tsx | 87 +--------- src/ui/overrides.tsx | 12 +- worker/TldrawDurableObject.ts | 10 +- 10 files changed, 426 insertions(+), 401 deletions(-) diff --git a/src/routes/Board.tsx b/src/routes/Board.tsx index a6709a8..b5d43b1 100644 --- a/src/routes/Board.tsx +++ b/src/routes/Board.tsx @@ -25,7 +25,7 @@ import { ClickPropagator, } from "@/propagators/ScopedPropagators" import { SlideShapeTool } from "@/tools/SlideShapeTool" -import { SlideShapeUtil } from "@/shapes/SlideShapeUtil" +import { SlideShape } from "@/shapes/SlideShapeUtil" import { makeRealSettings, applySettingsMigrations } from "@/lib/settings" import { PromptShapeTool } from "@/tools/PromptShapeTool" import { PromptShape } from "@/shapes/PromptShapeUtil" @@ -38,7 +38,7 @@ const customShapeUtils = [ ChatBoxShape, VideoChatShape, EmbedShape, - SlideShapeUtil, + SlideShape, MycrozineTemplateShape, MarkdownShape, PromptShape, diff --git a/src/shapes/PromptShapeUtil.tsx b/src/shapes/PromptShapeUtil.tsx index 531a3cf..bc74207 100644 --- a/src/shapes/PromptShapeUtil.tsx +++ b/src/shapes/PromptShapeUtil.tsx @@ -1,165 +1,168 @@ import { - BaseBoxShapeUtil, - HTMLContainer, - TLBaseShape, - TLGeoShape, - TLShape, + BaseBoxShapeUtil, + HTMLContainer, + TLBaseShape, + TLGeoShape, + TLShape, } from "tldraw" import { getEdge } from "@/propagators/tlgraph" import { llm } from "@/utils/llm" import { isShapeOfType } from "@/propagators/utils" type IPrompt = TLBaseShape< - "prompt", - { - w: number - h: number - prompt: string - value: string - agentBinding: string | null - } + "Prompt", + { + w: number + h: number + prompt: string + value: string + agentBinding: string | null + } > export class PromptShape extends BaseBoxShapeUtil { - static override type = "prompt" as const + static override type = "Prompt" as const - FIXED_HEIGHT = 50 as const - MIN_WIDTH = 150 as const - PADDING = 4 as const + FIXED_HEIGHT = 50 as const + MIN_WIDTH = 150 as const + PADDING = 4 as const - getDefaultProps(): IPrompt["props"] { - return { - w: 300, - h: 50, - prompt: "", - value: "", - agentBinding: null, - } - } + getDefaultProps(): IPrompt["props"] { + return { + w: 300, + h: 50, + prompt: "", + value: "", + agentBinding: null, + } + } - // override onResize: TLResizeHandle = ( - // shape, - // { scaleX, initialShape }, - // ) => { - // const { x, y } = shape - // const w = initialShape.props.w * scaleX - // return { - // x, - // y, - // props: { - // ...shape.props, - // w: Math.max(Math.abs(w), this.MIN_WIDTH), - // h: this.FIXED_HEIGHT, - // }, - // } - // } + // override onResize: TLResizeHandle = ( + // shape, + // { scaleX, initialShape }, + // ) => { + // const { x, y } = shape + // const w = initialShape.props.w * scaleX + // return { + // x, + // y, + // props: { + // ...shape.props, + // w: Math.max(Math.abs(w), this.MIN_WIDTH), + // h: this.FIXED_HEIGHT, + // }, + // } + // } - component(shape: IPrompt) { - const arrowBindings = this.editor.getBindingsInvolvingShape( - shape.id, - "arrow", - ) - const arrows = arrowBindings - .map((binding) => this.editor.getShape(binding.fromId)) + component(shape: IPrompt) { + const arrowBindings = this.editor.getBindingsInvolvingShape( + shape.id, + "arrow", + ) + const arrows = arrowBindings.map((binding) => + this.editor.getShape(binding.fromId), + ) - const inputMap = arrows.reduce((acc, arrow) => { - const edge = getEdge(arrow, this.editor); - if (edge) { - const sourceShape = this.editor.getShape(edge.from); - if (sourceShape && edge.text) { - acc[edge.text] = sourceShape; - } - } - return acc; - }, {} as Record); + const inputMap = arrows.reduce((acc, arrow) => { + const edge = getEdge(arrow, this.editor) + if (edge) { + const sourceShape = this.editor.getShape(edge.from) + if (sourceShape && edge.text) { + acc[edge.text] = sourceShape + } + } + return acc + }, {} as Record) - const generateText = async (prompt: string) => { - await llm('', prompt, (partial: string, done: boolean) => { - console.log("DONE??", done) - this.editor.updateShape({ - id: shape.id, - type: "prompt", - props: { value: partial, agentBinding: done ? null : 'someone' }, - }) - }) - } + const generateText = async (prompt: string) => { + await llm("", prompt, (partial: string, done: boolean) => { + console.log("DONE??", done) + this.editor.updateShape({ + id: shape.id, + type: "Prompt", + props: { value: partial, agentBinding: done ? null : "someone" }, + }) + }) + } - const handlePrompt = () => { - if (shape.props.agentBinding) { - return - } - let processedPrompt = shape.props.prompt; - for (const [key, sourceShape] of Object.entries(inputMap)) { - const pattern = `{${key}}`; - if (processedPrompt.includes(pattern)) { - if (isShapeOfType(sourceShape, 'geo')) { - processedPrompt = processedPrompt.replace(pattern, sourceShape.props.text); - } - } - } - console.log(processedPrompt); - generateText(processedPrompt) - }; + const handlePrompt = () => { + if (shape.props.agentBinding) { + return + } + let processedPrompt = shape.props.prompt + for (const [key, sourceShape] of Object.entries(inputMap)) { + const pattern = `{${key}}` + if (processedPrompt.includes(pattern)) { + if (isShapeOfType(sourceShape, "geo")) { + processedPrompt = processedPrompt.replace( + pattern, + sourceShape.props.text, + ) + } + } + } + //console.log(processedPrompt) + generateText(processedPrompt) + } - return ( - - { - this.editor.updateShape({ - id: shape.id, - type: "prompt", - props: { prompt: text.target.value }, - }) - }} - /> - - - ) - } + return ( + + { + this.editor.updateShape({ + id: shape.id, + type: "Prompt", + props: { prompt: text.target.value }, + }) + }} + /> + + + ) + } - // [5] - indicator(shape: IPrompt) { - return - } -} \ No newline at end of file + indicator(shape: IPrompt) { + return + } +} diff --git a/src/shapes/SlideShapeUtil.tsx b/src/shapes/SlideShapeUtil.tsx index a2f76bb..97c2748 100644 --- a/src/shapes/SlideShapeUtil.tsx +++ b/src/shapes/SlideShapeUtil.tsx @@ -1,128 +1,136 @@ -import { useCallback } from 'react' +import { useCallback } from "react" import { - BaseBoxShapeUtil, - Geometry2d, - RecordProps, - Rectangle2d, - SVGContainer, - ShapeUtil, - T, - TLBaseShape, - getPerfectDashProps, - resizeBox, - useValue, -} from 'tldraw' -import { moveToSlide, useSlides } from '@/slides/useSlides' + BaseBoxShapeUtil, + Geometry2d, + RecordProps, + Rectangle2d, + SVGContainer, + ShapeUtil, + T, + TLBaseShape, + getPerfectDashProps, + resizeBox, + useValue, +} from "tldraw" +import { moveToSlide, useSlides } from "@/slides/useSlides" export type ISlideShape = TLBaseShape< - 'Slide', - { - w: number - h: number - } + "Slide", + { + w: number + h: number + } > -export class SlideShapeUtil extends BaseBoxShapeUtil { - static override type = "Slide" - - // static override props = { - // w: T.number, - // h: T.number, - // } +export class SlideShape extends BaseBoxShapeUtil { + static override type = "Slide" - override canBind = () => false - override hideRotateHandle = () => true + // static override props = { + // w: T.number, + // h: T.number, + // } - getDefaultProps(): ISlideShape["props"] { - return { - w: 720, - h: 480, - } - } + override canBind = () => false + override hideRotateHandle = () => true - getGeometry(shape: ISlideShape): Geometry2d { - return new Rectangle2d({ - width: shape.props.w, - height: shape.props.h, - isFilled: false, - }) - } + getDefaultProps(): ISlideShape["props"] { + return { + w: 720, + h: 480, + } + } - override onRotate = (initial: ISlideShape) => initial - override onResize(shape: ISlideShape, info: any) { - return resizeBox(shape, info) - } + getGeometry(shape: ISlideShape): Geometry2d { + return new Rectangle2d({ + width: shape.props.w, + height: shape.props.h, + isFilled: false, + }) + } - override onDoubleClick = (shape: ISlideShape) => { - moveToSlide(this.editor, shape) - this.editor.selectNone() - } + override onRotate = (initial: ISlideShape) => initial + override onResize(shape: ISlideShape, info: any) { + return resizeBox(shape, info) + } - override onDoubleClickEdge = (shape: ISlideShape) => { - moveToSlide(this.editor, shape) - this.editor.selectNone() - } + override onDoubleClick = (shape: ISlideShape) => { + moveToSlide(this.editor, shape) + this.editor.selectNone() + } - component(shape: ISlideShape) { - const bounds = this.editor.getShapeGeometry(shape).bounds + override onDoubleClickEdge = (shape: ISlideShape) => { + moveToSlide(this.editor, shape) + this.editor.selectNone() + } - // eslint-disable-next-line react-hooks/rules-of-hooks - const zoomLevel = useValue('zoom level', () => this.editor.getZoomLevel(), [this.editor]) + component(shape: ISlideShape) { + const bounds = this.editor.getShapeGeometry(shape).bounds - // eslint-disable-next-line react-hooks/rules-of-hooks - const slides = useSlides() - const index = slides.findIndex((s) => s.id === shape.id) + // eslint-disable-next-line react-hooks/rules-of-hooks + const zoomLevel = useValue("zoom level", () => this.editor.getZoomLevel(), [ + this.editor, + ]) - // eslint-disable-next-line react-hooks/rules-of-hooks - const handleLabelPointerDown = useCallback(() => this.editor.select(shape.id), [shape.id]) + // eslint-disable-next-line react-hooks/rules-of-hooks + const slides = useSlides() + const index = slides.findIndex((s) => s.id === shape.id) - if (!bounds) return null + // eslint-disable-next-line react-hooks/rules-of-hooks + const handleLabelPointerDown = useCallback( + () => this.editor.select(shape.id), + [shape.id], + ) - return ( - <> -
- {`Slide ${index + 1}`} -
- - - {bounds.sides.map((side, i) => { - const { strokeDasharray, strokeDashoffset } = getPerfectDashProps( - side[0].dist(side[1]), - 1 / zoomLevel, - { - style: 'dashed', - lengthRatio: 6, - } - ) + if (!bounds) return null - return ( - - ) - })} - - - - ) - } + return ( + <> +
+ {`Slide ${index + 1}`} +
+ + + {bounds.sides.map((side, i) => { + const { strokeDasharray, strokeDashoffset } = getPerfectDashProps( + side[0].dist(side[1]), + 1 / zoomLevel, + { + style: "dashed", + lengthRatio: 6, + }, + ) - indicator(shape: ISlideShape) { - return - } -} \ No newline at end of file + return ( + + ) + })} + + + + ) + } + + indicator(shape: ISlideShape) { + return + } +} diff --git a/src/tools/PromptShapeTool.ts b/src/tools/PromptShapeTool.ts index 51ba34f..987d24b 100644 --- a/src/tools/PromptShapeTool.ts +++ b/src/tools/PromptShapeTool.ts @@ -1,8 +1,8 @@ import { BaseBoxShapeTool } from 'tldraw' export class PromptShapeTool extends BaseBoxShapeTool { - static override id = 'prompt' + static override id = 'Prompt' static override initial = 'idle' - override shapeType = 'prompt' + override shapeType = 'Prompt' } \ No newline at end of file diff --git a/src/tools/SlideShapeTool.ts b/src/tools/SlideShapeTool.ts index 87746b7..710d978 100644 --- a/src/tools/SlideShapeTool.ts +++ b/src/tools/SlideShapeTool.ts @@ -7,6 +7,6 @@ export class SlideShapeTool extends BaseBoxShapeTool { constructor(editor: any) { super(editor) - console.log('SlideShapeTool constructed', { id: this.id, shapeType: this.shapeType }) + //console.log('SlideShapeTool constructed', { id: this.id, shapeType: this.shapeType }) } } \ No newline at end of file diff --git a/src/ui/CustomContextMenu.tsx b/src/ui/CustomContextMenu.tsx index dabf26d..1379f8b 100644 --- a/src/ui/CustomContextMenu.tsx +++ b/src/ui/CustomContextMenu.tsx @@ -142,7 +142,7 @@ export function CustomContextMenu(props: TLUiContextMenuProps) { }} /> + { + editor.setCurrentTool("Prompt") + }} + /> {/* Frame Controls */} diff --git a/src/ui/CustomToolbar.tsx b/src/ui/CustomToolbar.tsx index 1fc13ba..66f1d74 100644 --- a/src/ui/CustomToolbar.tsx +++ b/src/ui/CustomToolbar.tsx @@ -3,11 +3,15 @@ import { DefaultToolbar, DefaultToolbarContent } from "tldraw" import { useTools } from "tldraw" import { useEditor } from "tldraw" import { useState, useEffect } from "react" +import { useDialogs } from "tldraw" +import { SettingsDialog } from "./SettingsDialog" export function CustomToolbar() { const editor = useEditor() const tools = useTools() const [isReady, setIsReady] = useState(false) + const [hasApiKey, setHasApiKey] = useState(false) + const { addDialog, removeDialog } = useDialogs() useEffect(() => { if (editor && tools) { @@ -15,53 +19,132 @@ export function CustomToolbar() { } }, [editor, tools]) + useEffect(() => { + const settings = localStorage.getItem("jeff_keys") + if (settings) { + const { keys } = JSON.parse(settings) + setHasApiKey(Object.values(keys).some((key) => key)) + } + }, []) + if (!isReady) return null return ( - - - {tools["VideoChat"] && ( - - )} - {tools["ChatBox"] && ( - - )} - {tools["Embed"] && ( - - )} - {tools["SlideShape"] && ( - - )} - {/* - {tools["Markdown"] && ( - - )} - */} - +
+
+ +
+ + + {tools["VideoChat"] && ( + + )} + {tools["ChatBox"] && ( + + )} + {tools["Embed"] && ( + + )} + {tools["SlideShape"] && ( + + )} + {tools["Markdown"] && ( + + )} + {tools["MycrozineTemplate"] && ( + + )} + {tools["Prompt"] && ( + + )} + +
) } diff --git a/src/ui/components.tsx b/src/ui/components.tsx index f48e51f..2034958 100644 --- a/src/ui/components.tsx +++ b/src/ui/components.tsx @@ -4,102 +4,17 @@ import { CustomContextMenu } from "./CustomContextMenu" import { DefaultKeyboardShortcutsDialog, DefaultKeyboardShortcutsDialogContent, - DefaultToolbar, - DefaultToolbarContent, TLComponents, TldrawUiMenuItem, - useDialogs, - useIsToolSelected, useTools, } from "tldraw" -import { SettingsDialog } from "./SettingsDialog" -import { useEffect } from "react" import { SlidesPanel } from "@/slides/SlidesPanel" -import { useState } from "react" export const components: TLComponents = { - // Toolbar: CustomToolbar, + Toolbar: CustomToolbar, MainMenu: CustomMainMenu, ContextMenu: CustomContextMenu, HelperButtons: SlidesPanel, - Toolbar: (props: any) => { - const tools = useTools() - const slideTool = tools["Slide"] - const isSlideSelected = slideTool ? useIsToolSelected(slideTool) : false - const { addDialog, removeDialog } = useDialogs() - const [hasApiKey, setHasApiKey] = useState(false) - - useEffect(() => { - const key = localStorage.getItem("openai_api_key") - setHasApiKey(!!key) - }, []) - - return ( -
-
- ( - - ) -
- - {slideTool && ( - - )} - - -
- ) - }, KeyboardShortcutsDialog: (props: any) => { const tools = useTools() return ( diff --git a/src/ui/overrides.tsx b/src/ui/overrides.tsx index 1bc62bb..683424e 100644 --- a/src/ui/overrides.tsx +++ b/src/ui/overrides.tsx @@ -106,10 +106,7 @@ export const overrides: TLUiOverrides = { 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: { @@ -130,6 +127,15 @@ export const overrides: TLUiOverrides = { readonlyOk: true, onSelect: () => editor.setCurrentTool("MycrozineTemplate"), }, + Prompt: { + id: "Prompt", + icon: "prompt", + label: "Prompt", + type: "Prompt", + kdb: "p", + readonlyOk: true, + onSelect: () => editor.setCurrentTool("Prompt"), + }, hand: { ...tools.hand, onDoubleClick: (info: any) => { diff --git a/worker/TldrawDurableObject.ts b/worker/TldrawDurableObject.ts index 927d31f..2a467e2 100644 --- a/worker/TldrawDurableObject.ts +++ b/worker/TldrawDurableObject.ts @@ -17,8 +17,7 @@ import { VideoChatShape } from "@/shapes/VideoChatShapeUtil" import { EmbedShape } from "@/shapes/EmbedShapeUtil" import { MarkdownShape } from "@/shapes/MarkdownShapeUtil" import { MycrozineTemplateShape } from "@/shapes/MycrozineTemplateShapeUtil" -import { T } from "@tldraw/tldraw" -import { SlideShapeUtil } from "@/shapes/SlideShapeUtil" +import { SlideShape } from "@/shapes/SlideShapeUtil" // add custom shapes and bindings here if needed: export const customSchema = createTLSchema({ @@ -45,8 +44,8 @@ export const customSchema = createTLSchema({ migrations: MycrozineTemplateShape.migrations, }, Slide: { - props: SlideShapeUtil.props, - migrations: SlideShapeUtil.migrations, + props: SlideShape.props, + migrations: SlideShape.migrations, }, }, bindings: defaultBindingSchemas, @@ -225,6 +224,7 @@ export class TldrawDurableObject { onDataChange: () => { // and persist whenever the data in the room changes this.schedulePersistToR2() + console.log("Persisting", this.roomId, "to R2") }, }) })() @@ -237,7 +237,7 @@ export class TldrawDurableObject { schedulePersistToR2 = throttle(async () => { if (!this.roomPromise || !this.roomId) return const room = await this.getRoom() - + // convert the room to JSON and upload it to R2 const snapshot = JSON.stringify(room.getCurrentSnapshot()) await this.r2.put(`rooms/${this.roomId}`, snapshot)