diff --git a/app/CodeEditor/CodeEditor.tsx b/app/CodeEditor/CodeEditor.tsx
new file mode 100644
index 00000000..f91fbabd
--- /dev/null
+++ b/app/CodeEditor/CodeEditor.tsx
@@ -0,0 +1,117 @@
+import { Editor as MonacoEditor, OnChange } from '@monaco-editor/react'
+import { stopEventPropagation, track, useEditor, useIsDarkMode } from '@tldraw/tldraw'
+import { useState } from 'react'
+import { PreviewShape, showingEditor } from '../PreviewShape/PreviewShape'
+import { updateLink } from '../lib/uploadLink'
+
+export const EDITOR_WIDTH = 700
+
+export const CodeEditor = track(() => {
+ const editor = useEditor()
+ const dark = useIsDarkMode()
+ const bounds = editor.getViewportPageBounds()
+ const shape = editor.getOnlySelectedShape()
+ const previewShape = shape?.type === 'preview' ? (shape as PreviewShape) : undefined
+
+ const [value, setValue] = useState('')
+ const [isSaving, setIsSaving] = useState(false)
+ const showEditor = showingEditor.get()
+
+ const handleOnChange: OnChange = (value) => {
+ setValue(value)
+ }
+
+ if (!bounds || !previewShape || !showEditor) return null
+
+ return (
+ <>
+
stopEventPropagation(e)}
+ onKeyUp={async (e) => {
+ if (e.key === 's' && e.ctrlKey) {
+ setIsSaving(true)
+ if (!value && value === '') return
+ await updateLink(shape.id, value)
+ editor.updateShape
({
+ id: previewShape.id,
+ type: 'preview',
+ props: {
+ html: value,
+ linkUploadVersion: previewShape.props.linkUploadVersion + 1,
+ },
+ })
+ setIsSaving(false)
+ }
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+ >
+ )
+})
diff --git a/app/PreviewShape/PreviewShape.tsx b/app/PreviewShape/PreviewShape.tsx
index 719a86c4..7d910f50 100644
--- a/app/PreviewShape/PreviewShape.tsx
+++ b/app/PreviewShape/PreviewShape.tsx
@@ -1,22 +1,22 @@
/* eslint-disable react-hooks/rules-of-hooks */
import {
- TLBaseShape,
BaseBoxShapeUtil,
- useIsEditing,
- HTMLContainer,
- toDomPrecision,
- Icon,
- useToasts,
DefaultSpinner,
- stopEventPropagation,
+ HTMLContainer,
+ TLBaseShape,
Vec2d,
+ atom,
+ toDomPrecision,
+ useIsEditing,
useValue,
} from '@tldraw/tldraw'
+import { useEffect } from 'react'
+import { CopyToClipboardButton } from '../components/CopyToClipboardButton'
+import { Hint } from '../components/Hint'
+import { ShowEditorButton, showShapeNextToEditor } from '../components/ShowEditorButton'
import { UrlLinkButton } from '../components/UrlLinkButton'
import { LINK_HOST, PROTOCOL } from '../lib/hosts'
-import { useEffect } from 'react'
import { uploadLink } from '../lib/uploadLink'
-import style from 'styled-jsx/style'
export type PreviewShape = TLBaseShape<
'preview',
@@ -30,6 +30,8 @@ export type PreviewShape = TLBaseShape<
}
>
+export const showingEditor = atom('showingEditor', false)
+
export class PreviewShapeUtil extends BaseBoxShapeUtil {
static override type = 'preview' as const
@@ -50,7 +52,6 @@ export class PreviewShapeUtil extends BaseBoxShapeUtil {
override component(shape: PreviewShape) {
const isEditing = useIsEditing(shape.id)
- const toast = useToasts()
const boxShadow = useValue(
'box shadow',
@@ -86,7 +87,6 @@ export class PreviewShapeUtil extends BaseBoxShapeUtil {
}
}, [shape.id, html, linkUploadVersion, uploadedShapeId])
- console.log(shape, uploadedShapeId)
const isLoading = linkUploadVersion === undefined || uploadedShapeId !== shape.id
const uploadUrl = [PROTOCOL, LINK_HOST, '/', shape.id.replace(/^shape:/, '')].join('')
@@ -123,35 +123,17 @@ export class PreviewShapeUtil extends BaseBoxShapeUtil {
borderRadius: 'var(--radius-2)',
}}
/>
-
-
+
+
+
+
{
{isEditing ? 'Click the canvas to exit' : 'Double click to interact'}
+
>
)}
)
}
+ override onClick = (shape: PreviewShape) => {
+ if (!showingEditor.get()) return
+ showShapeNextToEditor(this.editor, shape)
+ }
+
indicator(shape: PreviewShape) {
return
}
diff --git a/app/components/CopyToClipboardButton.tsx b/app/components/CopyToClipboardButton.tsx
new file mode 100644
index 00000000..34bb8ea3
--- /dev/null
+++ b/app/components/CopyToClipboardButton.tsx
@@ -0,0 +1,33 @@
+import { Icon, stopEventPropagation, useToasts } from '@tldraw/tldraw'
+import { PreviewShape } from '../PreviewShape/PreviewShape'
+
+export function CopyToClipboardButton({ shape }: { shape: PreviewShape }) {
+ const toast = useToasts()
+ return (
+
+ )
+}
diff --git a/app/components/Hint.tsx b/app/components/Hint.tsx
new file mode 100644
index 00000000..13295099
--- /dev/null
+++ b/app/components/Hint.tsx
@@ -0,0 +1,31 @@
+export function Hint({ isEditing }: { isEditing: boolean }) {
+ return (
+
+
+ {isEditing ? 'Click the canvas to exit' : 'Double click to interact'}
+
+
+ )
+}
diff --git a/app/components/ShowEditorButton.tsx b/app/components/ShowEditorButton.tsx
new file mode 100644
index 00000000..233ec5bb
--- /dev/null
+++ b/app/components/ShowEditorButton.tsx
@@ -0,0 +1,45 @@
+'use client'
+import { Editor, Icon, TLShape, stopEventPropagation, track, useEditor } from '@tldraw/tldraw'
+import { PreviewShape, showingEditor } from '../PreviewShape/PreviewShape'
+import { EDITOR_WIDTH } from '../CodeEditor/CodeEditor'
+
+export const ShowEditorButton = track(({ shape }: { shape: PreviewShape }) => {
+ const showing = showingEditor.get()
+ const editor = useEditor()
+ return (
+
+ )
+})
+
+export function showShapeNextToEditor(editor: Editor, shape: TLShape) {
+ const bounds = editor.getViewportPageBounds()
+ editor.centerOnPoint(
+ {
+ x: shape.x + bounds.width / 2 - (EDITOR_WIDTH + 40) / editor.getZoomLevel(),
+ y: shape.y + bounds.height / 2 - 20 / editor.getZoomLevel(),
+ },
+ { duration: 320 }
+ )
+}
diff --git a/app/components/ShowResult.tsx b/app/components/ShowResult.tsx
new file mode 100644
index 00000000..fb2a44f7
--- /dev/null
+++ b/app/components/ShowResult.tsx
@@ -0,0 +1,30 @@
+import { toDomPrecision } from '@tldraw/tldraw'
+import { PreviewShape } from '../PreviewShape/PreviewShape'
+
+export function ShowResult({
+ boxShadow,
+ isEditing,
+ html,
+ shape,
+}: {
+ boxShadow: string
+ isEditing: boolean
+ html: string
+ shape: PreviewShape
+}) {
+ return (
+
+ )
+}
diff --git a/app/components/UrlLinkButton.tsx b/app/components/UrlLinkButton.tsx
index e49b093e..1821840d 100644
--- a/app/components/UrlLinkButton.tsx
+++ b/app/components/UrlLinkButton.tsx
@@ -9,9 +9,6 @@ export function UrlLinkButton({ uploadUrl }: { uploadUrl: string }) {