You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
160 lines
4.8 KiB
160 lines
4.8 KiB
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="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"); |
|
} |
|
}} |
|
/> |
|
</> |
|
); |
|
};
|
|
|