Markdown tool working, console log cleanup

This commit is contained in:
Jeff-Emmett 2025-03-15 14:57:57 -07:00
parent 4e83a577f0
commit b9addbe417
5 changed files with 154 additions and 179 deletions

View File

@ -184,7 +184,7 @@ async function sendMessageToChat(
}) })
const result = await response.text() const result = await response.text()
console.log("Message sent successfully:", result) //console.log("Message sent successfully:", result)
} catch (error) { } catch (error) {
console.error("Error sending message:", error) console.error("Error sending message:", error)
} }

View File

@ -1,188 +1,170 @@
/** TODO: build this */ import React from 'react'
import MDEditor from '@uiw/react-md-editor'
import { BaseBoxShapeUtil, TLBaseBoxShape, TLBaseShape, StyleProp, T, DefaultSizeStyle, DefaultFontStyle, DefaultColorStyle } from "tldraw" import { BaseBoxShapeUtil, TLBaseShape } from '@tldraw/tldraw'
import { useEffect, useRef, useState } from "react"
import { marked } from "marked"
// Uncomment and use these style definitions
const MarkdownColor = StyleProp.defineEnum('markdown:color', {
defaultValue: 'black',
values: ['black', 'blue', 'green', 'grey', 'light-blue', 'light-green', 'light-red', 'light-violet', 'orange', 'red', 'violet', 'yellow'],
})
const MarkdownSize = StyleProp.defineEnum('markdown:size', {
defaultValue: 'medium',
values: ['small', 'medium', 'large'],
})
const MarkdownFont = StyleProp.defineEnum('markdown:font', {
defaultValue: 'draw',
values: ['draw', 'sans', 'serif', 'mono'],
})
//const MarkdownHorizontalAlign = StyleProp.define('markdown:horizontalalign', { defaultValue: 'start' })
//const MarkdownVerticalAlign = StyleProp.define('markdown:verticalalign', { defaultValue: 'start' })
export type IMarkdownShape = TLBaseShape< export type IMarkdownShape = TLBaseShape<
"MarkdownTool", 'Markdown',
{ {
content: string
isPreview: boolean
w: number w: number
h: number h: number
color: string text: string
size: string
font: string
} }
> >
export class MarkdownShape extends BaseBoxShapeUtil< export class MarkdownShape extends BaseBoxShapeUtil<IMarkdownShape> {
IMarkdownShape & TLBaseBoxShape static type = 'Markdown' as const
> {
static override type = "MarkdownTool"
styles = { getDefaultProps(): IMarkdownShape['props'] {
color: MarkdownColor, return {
size: MarkdownSize, w: 300,
font: MarkdownFont, h: 200,
} text: '',
}
getDefaultProps(): IMarkdownShape["props"] & { w: number; h: number } {
console.log('getDefaultProps called');
const props = {
content: "",
isPreview: false,
w: 400,
h: 300,
color: 'black',
size: 'medium',
font: 'draw'
};
console.log('Default props:', props);
return props;
}
indicator(shape: IMarkdownShape) {
return (
<g>
<rect x={0} y={0} width={shape.props.w} height={shape.props.h} />
</g>
)
} }
component(shape: IMarkdownShape) { component(shape: IMarkdownShape) {
console.log('Component rendering with shape:', shape); // Hooks must be at the top level
console.log('Available styles:', this.styles); const isSelected = this.editor.getSelectedShapeIds().includes(shape.id)
const editor = this.editor const markdownRef = React.useRef<HTMLDivElement>(null)
return <MarkdownEditor shape={shape} editor={editor} />
} // Single useEffect hook that handles checkbox interactivity
} React.useEffect(() => {
if (!isSelected && markdownRef.current) {
function MarkdownEditor({ shape, editor }: { shape: IMarkdownShape; editor: any }) { const checkboxes = markdownRef.current.querySelectorAll('input[type="checkbox"]')
console.log('MarkdownEditor mounted with shape:', shape); checkboxes.forEach((checkbox) => {
console.log('Editor instance:', editor); checkbox.removeAttribute('disabled')
checkbox.addEventListener('click', handleCheckboxClick)
const textareaRef = useRef<HTMLTextAreaElement>(null) })
const [isPreview, setIsPreview] = useState(shape.props.isPreview)
const [renderedContent, setRenderedContent] = useState("") // Cleanup function
return () => {
useEffect(() => { if (markdownRef.current) {
if (textareaRef.current && textareaRef.current.value !== shape.props.content) { const checkboxes = markdownRef.current.querySelectorAll('input[type="checkbox"]')
textareaRef.current.value = shape.props.content checkboxes.forEach((checkbox) => {
} checkbox.removeEventListener('click', handleCheckboxClick)
}, [shape.props.content]) })
}
useEffect(() => { }
const html = marked.parse(shape.props.content, { breaks: true }) as string
setRenderedContent(html)
}, [shape.props.content])
const togglePreview = () => {
const newPreviewState = !isPreview
setIsPreview(newPreviewState)
editor.updateShape(shape.id, {
props: {
...shape.props,
isPreview: newPreviewState
} }
}) }, [isSelected, shape.props.text])
}
return ( // Handler function defined outside useEffect
<div style={{ const handleCheckboxClick = (event: Event) => {
event.stopPropagation()
const target = event.target as HTMLInputElement
const checked = target.checked
const text = shape.props.text
const lines = text.split('\n')
const checkboxRegex = /^\s*[-*+]\s+\[([ x])\]/
const newText = lines.map(line => {
if (line.includes(target.parentElement?.textContent || '')) {
return line.replace(checkboxRegex, `- [${checked ? 'x' : ' '}]`)
}
return line
}).join('\n')
this.editor.updateShape<IMarkdownShape>({
id: shape.id,
type: 'Markdown',
props: {
...shape.props,
text: newText,
},
})
}
const wrapperStyle: React.CSSProperties = {
width: '100%', width: '100%',
height: '100%', height: '100%',
display: 'flex',
flexDirection: 'column',
backgroundColor: 'white', backgroundColor: 'white',
border: '1px solid #e0e0e0', border: '1px solid #ddd',
borderRadius: '4px', borderRadius: '4px',
overflow: 'hidden' overflow: 'hidden',
}}> }
{/* Toolbar */}
<div style={{
padding: '4px 8px',
borderBottom: '1px solid #e0e0e0',
backgroundColor: '#f5f5f5',
display: 'flex',
gap: '8px',
alignItems: 'center'
}}>
<button
onClick={togglePreview}
style={{
padding: '4px 8px',
border: '1px solid #ccc',
borderRadius: '4px',
backgroundColor: 'white',
cursor: 'pointer',
fontSize: '12px'
}}
>
{isPreview ? 'Edit' : 'Preview'}
</button>
</div>
{/* Editor/Preview Area */} // Simplified contentStyle - removed padding and center alignment
<div style={{ const contentStyle: React.CSSProperties = {
flex: 1, width: '100%',
overflow: 'auto', height: '100%',
position: 'relative' backgroundColor: '#FFFFFF',
}}> cursor: isSelected ? 'text' : 'default',
{isPreview ? ( pointerEvents: 'all',
<div }
style={{
padding: '8px', // Show MDEditor when selected
height: '100%', if (isSelected) {
overflow: 'auto' return (
}} <div style={wrapperStyle}>
dangerouslySetInnerHTML={{ <div style={contentStyle}>
__html: marked(shape.props.content, { breaks: true }) as string <MDEditor
}} value={shape.props.text}
/> onChange={(value = '') => {
) : ( this.editor.updateShape<IMarkdownShape>({
<textarea id: shape.id,
ref={textareaRef} type: 'Markdown',
defaultValue={shape.props.content} props: {
style={{ ...shape.props,
width: '100%', text: value,
height: '100%', },
resize: 'none', })
border: 'none', }}
padding: '8px', preview='edit'
fontFamily: 'inherit', hideToolbar={true}
}} style={{
onChange={(e) => { height: '100%',
editor.updateShape(shape.id, { border: 'none',
props: { }}
...shape.props, textareaProps={{
content: e.target.value placeholder: "Enter markdown text...",
style: {
backgroundColor: 'transparent',
height: '100%',
padding: '12px',
lineHeight: '1.5',
fontSize: '14px',
} }
}) }}
}} onPointerDown={(e) => {
/> e.stopPropagation()
)} }}
/>
</div>
</div>
)
}
// Show rendered markdown when not selected
return (
<div style={wrapperStyle}>
<div style={contentStyle}>
<div ref={markdownRef} style={{ width: '100%', height: '100%', padding: '12px' }}>
{shape.props.text ? (
<MDEditor.Markdown source={shape.props.text} />
) : (
<span style={{ opacity: 0.5 }}>Click to edit markdown...</span>
)}
</div>
</div>
</div> </div>
</div> )
) }
indicator(shape: IMarkdownShape) {
return <rect width={shape.props.w} height={shape.props.h} />
}
// Add handlers for better interaction
override onDoubleClick = (shape: IMarkdownShape) => {
const textarea = document.querySelector(`[data-shape-id="${shape.id}"] textarea`) as HTMLTextAreaElement
textarea?.focus()
}
onPointerDown = (shape: IMarkdownShape) => {
if (!shape.props.text) {
const textarea = document.querySelector(`[data-shape-id="${shape.id}"] textarea`) as HTMLTextAreaElement
textarea?.focus()
}
}
} }

View File

@ -17,7 +17,7 @@ export class MycrozineTemplateShape extends BaseBoxShapeUtil<IMycrozineTemplateS
w: 2550, w: 2550,
h: 3300, h: 3300,
} }
console.log('MycrozineTemplate - Default props:', props) //console.log('MycrozineTemplate - Default props:', props)
return props return props
} }

View File

@ -20,7 +20,6 @@ const storeCameraPosition = (editor: Editor) => {
if (cameraHistory.length > MAX_HISTORY) { if (cameraHistory.length > MAX_HISTORY) {
cameraHistory.shift() cameraHistory.shift()
} }
console.log("Stored camera position:", currentCamera)
} }
} }
@ -112,18 +111,17 @@ export const revertCamera = (editor: Editor) => {
}, },
}) })
console.log("Reverted to camera position:", previousCamera) //console.log("Reverted to camera position:", previousCamera)
} }
} else { } else {
console.log("No camera history available") //console.log("No camera history available")
} }
} }
export const copyLinkToCurrentView = async (editor: Editor) => { export const copyLinkToCurrentView = async (editor: Editor) => {
console.log("Starting copyLinkToCurrentView")
if (!editor.store.serialize()) { if (!editor.store.serialize()) {
console.warn("Store not ready") //console.warn("Store not ready")
return return
} }
@ -143,7 +141,6 @@ export const copyLinkToCurrentView = async (editor: Editor) => {
} }
const finalUrl = url.toString() const finalUrl = url.toString()
console.log("Final URL to copy:", finalUrl)
if (navigator.clipboard && window.isSecureContext) { if (navigator.clipboard && window.isSecureContext) {
await navigator.clipboard.writeText(finalUrl) await navigator.clipboard.writeText(finalUrl)
@ -154,12 +151,10 @@ export const copyLinkToCurrentView = async (editor: Editor) => {
try { try {
await navigator.clipboard.writeText(textArea.value) await navigator.clipboard.writeText(textArea.value)
} catch (err) { } catch (err) {
console.error("Clipboard API failed:", err)
} }
document.body.removeChild(textArea) document.body.removeChild(textArea)
} }
} catch (error) { } catch (error) {
console.error("Failed to copy to clipboard:", error)
alert("Failed to copy link. Please check clipboard permissions.") alert("Failed to copy link. Please check clipboard permissions.")
} }
} }
@ -296,8 +291,6 @@ export const setInitialCameraFromUrl = (editor: Editor) => {
const shapeId = url.searchParams.get("shapeId") const shapeId = url.searchParams.get("shapeId")
const frameId = url.searchParams.get("frameId") const frameId = url.searchParams.get("frameId")
console.log('Setting initial camera from URL:', { x, y, zoom, shapeId, frameId })
if (x && y && zoom) { if (x && y && zoom) {
editor.stopCameraAnimation() editor.stopCameraAnimation()
editor.setCamera( editor.setCamera(

View File

@ -28,6 +28,6 @@ export async function llm(
partial += chunk.choices[0]?.delta?.content || ""; partial += chunk.choices[0]?.delta?.content || "";
onToken(partial, false); onToken(partial, false);
} }
console.log("Generated:", partial); //console.log("Generated:", partial);
onToken(partial, true); onToken(partial, true);
} }