feat: initial testing version
parent
42d8c5a040
commit
0958241589
@ -0,0 +1,589 @@
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { t } from "../../i18n";
|
||||
import { useApp } from "../App";
|
||||
import { Dialog } from "../Dialog";
|
||||
import { TextField } from "../TextField";
|
||||
import Trans from "../Trans";
|
||||
import {
|
||||
CloseIcon,
|
||||
RedoIcon,
|
||||
ZoomInIcon,
|
||||
ZoomOutIcon,
|
||||
playerPlayIcon,
|
||||
playerStopFilledIcon,
|
||||
} from "../icons";
|
||||
import { NonDeletedExcalidrawElement } from "../../element/types";
|
||||
import { convertToExcalidrawElements } from "../../data/transform";
|
||||
import { exportToCanvas } from "../../packages/utils";
|
||||
import { DEFAULT_EXPORT_PADDING } from "../../constants";
|
||||
import { canvasToBlob } from "../../data/blob";
|
||||
|
||||
const testResponse = `{
|
||||
"error": false,
|
||||
"data": [
|
||||
{
|
||||
"type": "ellipse",
|
||||
"x": 200,
|
||||
"y": 200,
|
||||
"width": 100,
|
||||
"height": 100,
|
||||
"strokeColor": "transparent",
|
||||
"backgroundColor": "yellow",
|
||||
"strokeWidth": 2
|
||||
},
|
||||
{
|
||||
"type": "line",
|
||||
"x": 300,
|
||||
"y": 250,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
70,
|
||||
0
|
||||
]
|
||||
],
|
||||
"width": -70,
|
||||
"height": 0,
|
||||
"strokeColor": "yellow",
|
||||
"backgroundColor": "transparent",
|
||||
"strokeWidth": 5
|
||||
},
|
||||
{
|
||||
"type": "line",
|
||||
"x": 293.30127018922195,
|
||||
"y": 275,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
60.62177826491069,
|
||||
35
|
||||
]
|
||||
],
|
||||
"width": -60.62177826491069,
|
||||
"height": -35,
|
||||
"strokeColor": "yellow",
|
||||
"backgroundColor": "transparent",
|
||||
"strokeWidth": 5
|
||||
},
|
||||
{
|
||||
"type": "line",
|
||||
"x": 275,
|
||||
"y": 293.30127018922195,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
35,
|
||||
60.62177826491069
|
||||
]
|
||||
],
|
||||
"width": -35,
|
||||
"height": -60.62177826491069,
|
||||
"strokeColor": "yellow",
|
||||
"backgroundColor": "transparent",
|
||||
"strokeWidth": 5
|
||||
},
|
||||
{
|
||||
"type": "line",
|
||||
"x": 250,
|
||||
"y": 300,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
0,
|
||||
70
|
||||
]
|
||||
],
|
||||
"width": 0,
|
||||
"height": -70,
|
||||
"strokeColor": "yellow",
|
||||
"backgroundColor": "transparent",
|
||||
"strokeWidth": 5
|
||||
},
|
||||
{
|
||||
"type": "line",
|
||||
"x": 225,
|
||||
"y": 293.30127018922195,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-34.99999999999997,
|
||||
60.62177826491069
|
||||
]
|
||||
],
|
||||
"width": -34.99999999999997,
|
||||
"height": -60.62177826491069,
|
||||
"strokeColor": "yellow",
|
||||
"backgroundColor": "transparent",
|
||||
"strokeWidth": 5
|
||||
},
|
||||
{
|
||||
"type": "line",
|
||||
"x": 206.69872981077805,
|
||||
"y": 275,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-60.62177826491069,
|
||||
35
|
||||
]
|
||||
],
|
||||
"width": -60.62177826491069,
|
||||
"height": -35,
|
||||
"strokeColor": "yellow",
|
||||
"backgroundColor": "transparent",
|
||||
"strokeWidth": 5
|
||||
},
|
||||
{
|
||||
"type": "line",
|
||||
"x": 200,
|
||||
"y": 250,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-70,
|
||||
2.842170943040401e-14
|
||||
]
|
||||
],
|
||||
"width": -70,
|
||||
"height": -2.842170943040401e-14,
|
||||
"strokeColor": "yellow",
|
||||
"backgroundColor": "transparent",
|
||||
"strokeWidth": 5
|
||||
},
|
||||
{
|
||||
"type": "line",
|
||||
"x": 206.69872981077805,
|
||||
"y": 225,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-60.62177826491069,
|
||||
-34.99999999999997
|
||||
]
|
||||
],
|
||||
"width": -60.62177826491069,
|
||||
"height": -34.99999999999997,
|
||||
"strokeColor": "yellow",
|
||||
"backgroundColor": "transparent",
|
||||
"strokeWidth": 5
|
||||
},
|
||||
{
|
||||
"type": "line",
|
||||
"x": 224.99999999999997,
|
||||
"y": 206.69872981077808,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-35.00000000000003,
|
||||
-60.62177826491069
|
||||
]
|
||||
],
|
||||
"width": -35.00000000000003,
|
||||
"height": -60.62177826491069,
|
||||
"strokeColor": "yellow",
|
||||
"backgroundColor": "transparent",
|
||||
"strokeWidth": 5
|
||||
},
|
||||
{
|
||||
"type": "line",
|
||||
"x": 250,
|
||||
"y": 200,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
-2.842170943040401e-14,
|
||||
-70
|
||||
]
|
||||
],
|
||||
"width": -2.842170943040401e-14,
|
||||
"height": -70,
|
||||
"strokeColor": "yellow",
|
||||
"backgroundColor": "transparent",
|
||||
"strokeWidth": 5
|
||||
},
|
||||
{
|
||||
"type": "line",
|
||||
"x": 275,
|
||||
"y": 206.69872981077808,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
35,
|
||||
-60.621778264910716
|
||||
]
|
||||
],
|
||||
"width": -35,
|
||||
"height": -60.621778264910716,
|
||||
"strokeColor": "yellow",
|
||||
"backgroundColor": "transparent",
|
||||
"strokeWidth": 5
|
||||
},
|
||||
{
|
||||
"type": "line",
|
||||
"x": 293.3012701892219,
|
||||
"y": 224.99999999999997,
|
||||
"points": [
|
||||
[
|
||||
0,
|
||||
0
|
||||
],
|
||||
[
|
||||
60.621778264910745,
|
||||
-35.00000000000003
|
||||
]
|
||||
],
|
||||
"width": -60.621778264910745,
|
||||
"height": -35.00000000000003,
|
||||
"strokeColor": "yellow",
|
||||
"backgroundColor": "transparent",
|
||||
"strokeWidth": 5
|
||||
}
|
||||
]
|
||||
}`;
|
||||
|
||||
async function fetchData(
|
||||
prompt: string,
|
||||
): Promise<readonly NonDeletedExcalidrawElement[]> {
|
||||
const response = await fetch(
|
||||
`http://localhost:3015/v1/ai/text-to-excalidraw/generate`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({ prompt }),
|
||||
},
|
||||
);
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.error) {
|
||||
alert("Oops!");
|
||||
return [];
|
||||
}
|
||||
|
||||
return convertToExcalidrawElements(result.data);
|
||||
}
|
||||
|
||||
export const TextToExcalidraw = () => {
|
||||
const app = useApp();
|
||||
|
||||
const [prompt, setPrompt] = useState("");
|
||||
const [isPanelOpen, setPanelOpen] = useState(false);
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
const [data, setData] = useState<
|
||||
readonly NonDeletedExcalidrawElement[] | null
|
||||
>(null);
|
||||
|
||||
const [previewCanvas, setPreviewCanvas] = useState<HTMLCanvasElement | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const onClose = () => {
|
||||
app.setOpenDialog(null);
|
||||
};
|
||||
|
||||
const onSubmit = async () => {
|
||||
setPanelOpen(true);
|
||||
setLoading(true);
|
||||
|
||||
const elements = await fetchData(prompt);
|
||||
|
||||
setData(elements);
|
||||
|
||||
const canvas = await exportToCanvas({
|
||||
elements,
|
||||
files: {},
|
||||
exportPadding: DEFAULT_EXPORT_PADDING,
|
||||
});
|
||||
|
||||
await canvasToBlob(canvas);
|
||||
|
||||
setPreviewCanvas(canvas);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
const onInsert = async () => {
|
||||
if (data) {
|
||||
app.addElementsFromPasteOrLibrary({
|
||||
elements: data,
|
||||
files: {},
|
||||
position: "center",
|
||||
fitToContent: true,
|
||||
});
|
||||
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (containerRef.current && previewCanvas) {
|
||||
containerRef.current.replaceChildren(previewCanvas);
|
||||
}
|
||||
}, [previewCanvas]);
|
||||
|
||||
// exportToCanvas([], {}, {}, {});
|
||||
// exportToSvg([], {exportBackground}, {}, {})
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
top: "6.5rem",
|
||||
pointerEvents: "auto",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "0.75rem",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="Island"
|
||||
style={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
boxSizing: "border-box",
|
||||
gap: "0.75rem",
|
||||
alignItems: "center",
|
||||
height: 48,
|
||||
padding: "0.5rem",
|
||||
}}
|
||||
>
|
||||
<input
|
||||
value={prompt}
|
||||
onChange={(e) => setPrompt(e.target.value)}
|
||||
type="text"
|
||||
style={{
|
||||
flexGrow: 1,
|
||||
height: "100%",
|
||||
boxSizing: "border-box",
|
||||
border: 0,
|
||||
outline: "none",
|
||||
}}
|
||||
placeholder="How can I help you today?"
|
||||
/>
|
||||
<button
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
height: "100%",
|
||||
border: "none",
|
||||
background: "white",
|
||||
aspectRatio: "1/1",
|
||||
padding: 0,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{ width: "1.25rem", height: "1.25rem", color: "#1B1B1F" }}
|
||||
>
|
||||
{CloseIcon}
|
||||
</div>
|
||||
</button>
|
||||
<div
|
||||
style={{ background: "#D6D6D6", width: 1, height: "1.5rem" }}
|
||||
></div>
|
||||
<button
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
height: "100%",
|
||||
border: "none",
|
||||
aspectRatio: "1/1",
|
||||
padding: 0,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#6965DB",
|
||||
borderRadius: "0.5rem",
|
||||
}}
|
||||
onClick={onSubmit}
|
||||
>
|
||||
<div style={{ width: "1.25rem", height: "1.25rem", color: "white" }}>
|
||||
{isLoading ? playerStopFilledIcon : playerPlayIcon}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{isPanelOpen && (
|
||||
<div
|
||||
className="Island"
|
||||
style={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
height: 400,
|
||||
boxSizing: "border-box",
|
||||
}}
|
||||
>
|
||||
{isLoading ? (
|
||||
"loading"
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={containerRef}
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
borderTop: "1px solid #F0EFFF",
|
||||
padding: "0.75rem",
|
||||
display: "flex",
|
||||
flexDirection: "row",
|
||||
gap: "0.75rem",
|
||||
}}
|
||||
>
|
||||
<button
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
width: 32,
|
||||
height: "100%",
|
||||
border: "none",
|
||||
aspectRatio: "1/1",
|
||||
padding: 0,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#F5F5F9",
|
||||
borderRadius: "0.5rem",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "1.25rem",
|
||||
height: "1.25rem",
|
||||
color: "#1B1B1F",
|
||||
}}
|
||||
>
|
||||
{RedoIcon}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
<div style={{ width: 32, height: "100%", display: "flex" }}>
|
||||
<button
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
width: 32,
|
||||
height: "100%",
|
||||
border: "none",
|
||||
aspectRatio: "1/1",
|
||||
padding: 0,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#F5F5F9",
|
||||
borderRadius: "0.5rem 0 0 0.5rem",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "1.25rem",
|
||||
height: "1.25rem",
|
||||
color: "#1B1B1F",
|
||||
}}
|
||||
>
|
||||
{ZoomOutIcon}
|
||||
</div>
|
||||
</button>
|
||||
<button
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
width: 32,
|
||||
height: "100%",
|
||||
border: "none",
|
||||
aspectRatio: "1/1",
|
||||
padding: 0,
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#F5F5F9",
|
||||
borderRadius: "0 0.5rem 0.5rem 0",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: "1.25rem",
|
||||
height: "1.25rem",
|
||||
color: "#1B1B1F",
|
||||
}}
|
||||
>
|
||||
{ZoomInIcon}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<div style={{ flexGrow: 1 }}></div>
|
||||
<button
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
height: "100%",
|
||||
border: "none",
|
||||
padding: "0.5rem 1rem",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
backgroundColor: "#6965DB",
|
||||
borderRadius: "0.5rem",
|
||||
color: "white",
|
||||
}}
|
||||
onClick={onInsert}
|
||||
>
|
||||
Insert into scene >
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue