Markdown tool working, console log cleanup
This commit is contained in:
parent
4e83a577f0
commit
b9addbe417
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue