markdown editor failing at sync
This commit is contained in:
parent
6f4dcb1bcb
commit
2fc965da28
|
|
@ -15,21 +15,23 @@
|
||||||
"author": "Jeff Emmett",
|
"author": "Jeff Emmett",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@hackmd/markdown-it": "^12.0.23",
|
||||||
|
"@hackmd/markdown-it-task-lists": "^2.1.4",
|
||||||
"@tldraw/assets": "^3.6.0",
|
"@tldraw/assets": "^3.6.0",
|
||||||
"@tldraw/sync": "^3.6.0",
|
"@tldraw/sync": "^3.6.0",
|
||||||
"@tldraw/sync-core": "^3.6.0",
|
"@tldraw/sync-core": "^3.6.0",
|
||||||
"@tldraw/tldraw": "^3.6.0",
|
"@tldraw/tldraw": "^3.6.0",
|
||||||
"@tldraw/tlschema": "^3.6.0",
|
"@tldraw/tlschema": "^3.6.0",
|
||||||
"@types/markdown-it": "^14.1.1",
|
|
||||||
"@vercel/analytics": "^1.2.2",
|
"@vercel/analytics": "^1.2.2",
|
||||||
"cloudflare-workers-unfurl": "^0.0.7",
|
"cloudflare-workers-unfurl": "^0.0.7",
|
||||||
"gray-matter": "^4.0.3",
|
"gray-matter": "^4.0.3",
|
||||||
"itty-router": "^5.0.17",
|
"itty-router": "^5.0.17",
|
||||||
"lodash.throttle": "^4.1.1",
|
"lodash.throttle": "^4.1.1",
|
||||||
"markdown-it": "^14.1.0",
|
"markdown-it": "^14.1.0",
|
||||||
|
"markdown-it-task-lists": "^2.1.1",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-markdown-editor-lite": "^1.3.4",
|
"react-markdown": "^9.0.1",
|
||||||
"react-router-dom": "^7.0.2",
|
"react-router-dom": "^7.0.2",
|
||||||
"tldraw": "^3.6.0",
|
"tldraw": "^3.6.0",
|
||||||
"vercel": "^39.1.1"
|
"vercel": "^39.1.1"
|
||||||
|
|
@ -38,8 +40,10 @@
|
||||||
"@cloudflare/types": "^6.0.0",
|
"@cloudflare/types": "^6.0.0",
|
||||||
"@cloudflare/workers-types": "^4.20240821.1",
|
"@cloudflare/workers-types": "^4.20240821.1",
|
||||||
"@types/lodash.throttle": "^4",
|
"@types/lodash.throttle": "^4",
|
||||||
|
"@types/markdown-it": "^14.1.2",
|
||||||
"@types/react": "^19.0.1",
|
"@types/react": "^19.0.1",
|
||||||
"@types/react-dom": "^19.0.1",
|
"@types/react-dom": "^19.0.1",
|
||||||
|
"@types/styled-components": "^5.1.34",
|
||||||
"@vitejs/plugin-react": "^4.0.3",
|
"@vitejs/plugin-react": "^4.0.3",
|
||||||
"@vitejs/plugin-react-swc": "^3.6.0",
|
"@vitejs/plugin-react-swc": "^3.6.0",
|
||||||
"concurrently": "^9.1.0",
|
"concurrently": "^9.1.0",
|
||||||
|
|
|
||||||
|
|
@ -326,11 +326,10 @@ p:has(+ ol) {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rc-md-editor {
|
|
||||||
border: none !important;
|
.mdx-editor {
|
||||||
|
height: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
outline: none;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rc-md-editor .editor-container {
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
@ -1,83 +1,163 @@
|
||||||
/** TODO: build this */
|
|
||||||
|
|
||||||
import { BaseBoxShapeUtil, TLBaseBoxShape, TLBaseShape } from "tldraw"
|
import { BaseBoxShapeUtil, TLBaseBoxShape, TLBaseShape } from "tldraw"
|
||||||
import MdEditor from "react-markdown-editor-lite"
|
import ReactMarkdown from "react-markdown"
|
||||||
import MarkdownIt from "markdown-it"
|
import { useCallback, useState, useEffect } from "react"
|
||||||
import "react-markdown-editor-lite/lib/index.css"
|
|
||||||
|
|
||||||
// Initialize markdown parser
|
|
||||||
const mdParser = new MarkdownIt()
|
|
||||||
|
|
||||||
export type IMarkdownShape = TLBaseShape<
|
export type IMarkdownShape = TLBaseShape<
|
||||||
"MarkdownTool",
|
"markdown",
|
||||||
{
|
{
|
||||||
content: string
|
content: string
|
||||||
html: string
|
w: number
|
||||||
|
h: number
|
||||||
|
isEditing: boolean
|
||||||
|
fill: string
|
||||||
|
color: string
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
export class MarkdownShape extends BaseBoxShapeUtil<
|
export class MarkdownShape extends BaseBoxShapeUtil<
|
||||||
IMarkdownShape & TLBaseBoxShape
|
IMarkdownShape & TLBaseBoxShape
|
||||||
> {
|
> {
|
||||||
static override type = "MarkdownTool"
|
static override type = "Markdown"
|
||||||
|
static override props = {
|
||||||
indicator(_shape: IMarkdownShape) {
|
content: {
|
||||||
return null
|
type: "string",
|
||||||
|
value: "# New Note",
|
||||||
|
validate: (value: unknown) => typeof value === "string",
|
||||||
|
},
|
||||||
|
w: {
|
||||||
|
type: "number",
|
||||||
|
value: 400,
|
||||||
|
validate: (value: unknown) => typeof value === "number",
|
||||||
|
},
|
||||||
|
h: {
|
||||||
|
type: "number",
|
||||||
|
value: 300,
|
||||||
|
validate: (value: unknown) => typeof value === "number",
|
||||||
|
},
|
||||||
|
isEditing: {
|
||||||
|
type: "boolean",
|
||||||
|
value: false,
|
||||||
|
validate: (value: unknown) => typeof value === "boolean",
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
type: "string",
|
||||||
|
value: "white",
|
||||||
|
validate: (value: unknown) => typeof value === "string",
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: "string",
|
||||||
|
value: "black",
|
||||||
|
validate: (value: unknown) => typeof value === "string",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
getDefaultProps(): IMarkdownShape["props"] & { w: number; h: number } {
|
override getDefaultProps(): IMarkdownShape["props"] &
|
||||||
|
TLBaseBoxShape["props"] {
|
||||||
return {
|
return {
|
||||||
content: "# New Note",
|
|
||||||
html: "<h1>New Note</h1>",
|
|
||||||
w: 400,
|
w: 400,
|
||||||
h: 300,
|
h: 300,
|
||||||
|
content: "# New Note",
|
||||||
|
isEditing: false,
|
||||||
|
fill: "white",
|
||||||
|
color: "black",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getStyleProps(shape: IMarkdownShape) {
|
||||||
|
return {
|
||||||
|
fill: shape.props.fill,
|
||||||
|
color: shape.props.color,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override indicator(shape: IMarkdownShape & TLBaseBoxShape) {
|
||||||
|
return {
|
||||||
|
x: shape.x,
|
||||||
|
y: shape.y,
|
||||||
|
width: shape.props.w,
|
||||||
|
height: shape.props.h,
|
||||||
|
rotation: shape.rotation,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
component(shape: IMarkdownShape & TLBaseBoxShape) {
|
component(shape: IMarkdownShape & TLBaseBoxShape) {
|
||||||
const handleEditorChange = ({
|
const [isEditing, setIsEditing] = useState(shape.props.isEditing)
|
||||||
text,
|
|
||||||
html,
|
const handleChange = useCallback(
|
||||||
}: {
|
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
|
||||||
text: string
|
this.editor?.updateShape({
|
||||||
html: string
|
id: shape.id,
|
||||||
}) => {
|
type: "markdown",
|
||||||
// Update the shape's content
|
props: {
|
||||||
|
...shape.props,
|
||||||
|
content: e.target.value,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
},
|
||||||
|
[shape.id],
|
||||||
|
)
|
||||||
|
|
||||||
|
const toggleEdit = useCallback(() => {
|
||||||
|
setIsEditing(!isEditing)
|
||||||
this.editor?.updateShape({
|
this.editor?.updateShape({
|
||||||
id: shape.id,
|
id: shape.id,
|
||||||
type: "MarkdownTool",
|
type: "markdown",
|
||||||
props: {
|
props: {
|
||||||
...shape.props,
|
...shape.props,
|
||||||
content: text,
|
isEditing: !isEditing,
|
||||||
html: html,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}, [isEditing, shape.id, shape.props])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (isEditing) {
|
||||||
|
this.editor?.updateShape({
|
||||||
|
id: shape.id,
|
||||||
|
type: "markdown",
|
||||||
|
props: {
|
||||||
|
...shape.props,
|
||||||
|
isEditing: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [shape.id, isEditing])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
position: "relative",
|
||||||
background: "white",
|
background: "white",
|
||||||
borderRadius: "4px",
|
borderRadius: "4px",
|
||||||
|
padding: "8px",
|
||||||
|
overflow: "auto",
|
||||||
|
cursor: "pointer",
|
||||||
}}
|
}}
|
||||||
|
onDoubleClick={toggleEdit}
|
||||||
>
|
>
|
||||||
<MdEditor
|
{isEditing ? (
|
||||||
style={{ height: "100%" }}
|
<textarea
|
||||||
renderHTML={(text) => mdParser.render(text)}
|
value={shape.props.content}
|
||||||
value={shape.props.content}
|
onChange={handleChange}
|
||||||
onChange={handleEditorChange}
|
style={{
|
||||||
view={{ menu: true, md: true, html: false }}
|
width: "100%",
|
||||||
canView={{
|
height: "100%",
|
||||||
menu: true,
|
border: "none",
|
||||||
md: true,
|
outline: "none",
|
||||||
html: false,
|
resize: "none",
|
||||||
both: false,
|
fontFamily: "inherit",
|
||||||
fullScreen: false,
|
fontSize: "inherit",
|
||||||
hideMenu: false,
|
backgroundColor: "transparent",
|
||||||
}}
|
}}
|
||||||
/>
|
autoFocus
|
||||||
|
onBlur={toggleEdit}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<ReactMarkdown>{shape.props.content}</ReactMarkdown>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { BaseBoxShapeTool } from "tldraw"
|
import { BaseBoxShapeTool } from "tldraw"
|
||||||
|
|
||||||
export class MarkdownTool extends BaseBoxShapeTool {
|
export class MarkdownTool extends BaseBoxShapeTool {
|
||||||
static override id = "MarkdownTool"
|
static override id = "markdown"
|
||||||
shapeType = "MarkdownTool"
|
shapeType = "markdown"
|
||||||
override initial = "idle"
|
override initial = "idle"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,12 +99,12 @@ export function CustomContextMenu(props: TLUiContextMenuProps) {
|
||||||
/>
|
/>
|
||||||
<TldrawUiMenuItem
|
<TldrawUiMenuItem
|
||||||
id="markdown"
|
id="markdown"
|
||||||
label="Create Markdown"
|
label="Create Markdown Box"
|
||||||
icon="markdown"
|
icon="markdown"
|
||||||
kbd="alt+m"
|
kbd="alt+m"
|
||||||
disabled={hasSelection}
|
disabled={hasSelection}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
editor.setCurrentTool("Markdown")
|
editor.setCurrentTool("markdown")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</TldrawUiMenuGroup>
|
</TldrawUiMenuGroup>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite"
|
||||||
import react from "@vitejs/plugin-react";
|
import react from "@vitejs/plugin-react"
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
envPrefix: ["VITE_"],
|
envPrefix: ["VITE_"],
|
||||||
|
|
@ -20,7 +20,7 @@ export default defineConfig({
|
||||||
},
|
},
|
||||||
define: {
|
define: {
|
||||||
"import.meta.env.VITE_WORKER_URL": JSON.stringify(
|
"import.meta.env.VITE_WORKER_URL": JSON.stringify(
|
||||||
process.env.VITE_WORKER_URL
|
process.env.VITE_WORKER_URL,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue