123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160 |
- import {
- DiagramToCodePlugin,
- exportToBlob,
- getTextFromElements,
- MIME_TYPES,
- TTDDialog,
- } from "@excalidraw/excalidraw";
- import { getDataURL } from "@excalidraw/excalidraw/data/blob";
- import { safelyParseJSON } from "@excalidraw/common";
- import type { ExcalidrawImperativeAPI } from "@excalidraw/excalidraw/types";
- export const AIComponents = ({
- excalidrawAPI,
- }: {
- excalidrawAPI: ExcalidrawImperativeAPI;
- }) => {
- return (
- <>
- <DiagramToCodePlugin
- generate={async ({ frame, children }) => {
- const appState = excalidrawAPI.getAppState();
- const blob = await exportToBlob({
- elements: children,
- appState: {
- ...appState,
- exportBackground: true,
- viewBackgroundColor: appState.viewBackgroundColor,
- },
- exportingFrame: frame,
- files: excalidrawAPI.getFiles(),
- mimeType: MIME_TYPES.jpg,
- });
- const dataURL = await getDataURL(blob);
- const textFromFrameChildren = getTextFromElements(children);
- const response = await fetch(
- `${
- import.meta.env.VITE_APP_AI_BACKEND
- }/v1/ai/diagram-to-code/generate`,
- {
- method: "POST",
- headers: {
- Accept: "application/json",
- "Content-Type": "application/json",
- },
- body: JSON.stringify({
- texts: textFromFrameChildren,
- image: dataURL,
- theme: appState.theme,
- }),
- },
- );
- if (!response.ok) {
- const text = await response.text();
- const errorJSON = safelyParseJSON(text);
- if (!errorJSON) {
- throw new Error(text);
- }
- if (errorJSON.statusCode === 429) {
- return {
- html: `<html>
- <body style="margin: 0; text-align: center">
- <div style="display: flex; align-items: center; justify-content: center; flex-direction: column; height: 100vh; padding: 0 60px">
- <div style="color:red">Too many requests today,</br>please try again tomorrow!</div>
- </br>
- </br>
- <div>You can also try <a href="${
- import.meta.env.VITE_APP_PLUS_LP
- }/plus?utm_source=excalidraw&utm_medium=app&utm_content=d2c" target="_blank" rel="noreferrer noopener">Excalidraw+</a> to get more requests.</div>
- </div>
- </body>
- </html>`,
- };
- }
- throw new Error(errorJSON.message || text);
- }
- try {
- const { html } = await response.json();
- if (!html) {
- throw new Error("Generation failed (invalid response)");
- }
- return {
- html,
- };
- } catch (error: any) {
- throw new Error("Generation failed (invalid response)");
- }
- }}
- />
- <TTDDialog
- onTextSubmit={async (input) => {
- try {
- const response = await fetch(
- `${
- import.meta.env.VITE_APP_AI_BACKEND
- }/v1/ai/text-to-diagram/generate`,
- {
- method: "POST",
- headers: {
- Accept: "application/json",
- "Content-Type": "application/json",
- },
- body: JSON.stringify({ prompt: input }),
- },
- );
- const rateLimit = response.headers.has("X-Ratelimit-Limit")
- ? parseInt(response.headers.get("X-Ratelimit-Limit") || "0", 10)
- : undefined;
- const rateLimitRemaining = response.headers.has(
- "X-Ratelimit-Remaining",
- )
- ? parseInt(
- response.headers.get("X-Ratelimit-Remaining") || "0",
- 10,
- )
- : undefined;
- const json = await response.json();
- if (!response.ok) {
- if (response.status === 429) {
- return {
- rateLimit,
- rateLimitRemaining,
- error: new Error(
- "Too many requests today, please try again tomorrow!",
- ),
- };
- }
- throw new Error(json.message || "Generation failed...");
- }
- const generatedResponse = json.generatedResponse;
- if (!generatedResponse) {
- throw new Error("Generation failed...");
- }
- return { generatedResponse, rateLimit, rateLimitRemaining };
- } catch (err: any) {
- throw new Error("Request failed");
- }
- }}
- />
- </>
- );
- };
|