AI.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import type { ExcalidrawImperativeAPI } from "../../packages/excalidraw/types";
  2. import {
  3. DiagramToCodePlugin,
  4. exportToBlob,
  5. getTextFromElements,
  6. MIME_TYPES,
  7. TTDDialog,
  8. } from "../../packages/excalidraw";
  9. import { getDataURL } from "../../packages/excalidraw/data/blob";
  10. import { safelyParseJSON } from "../../packages/excalidraw/utils";
  11. export const AIComponents = ({
  12. excalidrawAPI,
  13. }: {
  14. excalidrawAPI: ExcalidrawImperativeAPI;
  15. }) => {
  16. return (
  17. <>
  18. <DiagramToCodePlugin
  19. generate={async ({ frame, children }) => {
  20. const appState = excalidrawAPI.getAppState();
  21. const blob = await exportToBlob({
  22. data: {
  23. elements: children,
  24. appState: {
  25. ...appState,
  26. exportBackground: true,
  27. viewBackgroundColor: appState.viewBackgroundColor,
  28. },
  29. files: excalidrawAPI.getFiles(),
  30. },
  31. config: {
  32. exportingFrame: frame,
  33. mimeType: MIME_TYPES.jpg,
  34. },
  35. });
  36. const dataURL = await getDataURL(blob);
  37. const textFromFrameChildren = getTextFromElements(children);
  38. const response = await fetch(
  39. `${
  40. import.meta.env.VITE_APP_AI_BACKEND
  41. }/v1/ai/diagram-to-code/generate`,
  42. {
  43. method: "POST",
  44. headers: {
  45. Accept: "application/json",
  46. "Content-Type": "application/json",
  47. },
  48. body: JSON.stringify({
  49. texts: textFromFrameChildren,
  50. image: dataURL,
  51. theme: appState.theme,
  52. }),
  53. },
  54. );
  55. if (!response.ok) {
  56. const text = await response.text();
  57. const errorJSON = safelyParseJSON(text);
  58. if (!errorJSON) {
  59. throw new Error(text);
  60. }
  61. if (errorJSON.statusCode === 429) {
  62. return {
  63. html: `<html>
  64. <body style="margin: 0; text-align: center">
  65. <div style="display: flex; align-items: center; justify-content: center; flex-direction: column; height: 100vh; padding: 0 60px">
  66. <div style="color:red">Too many requests today,</br>please try again tomorrow!</div>
  67. </br>
  68. </br>
  69. <div>You can also try <a href="${
  70. import.meta.env.VITE_APP_PLUS_LP
  71. }/plus?utm_source=excalidraw&utm_medium=app&utm_content=d2c" target="_blank" rel="noreferrer noopener">Excalidraw+</a> to get more requests.</div>
  72. </div>
  73. </body>
  74. </html>`,
  75. };
  76. }
  77. throw new Error(errorJSON.message || text);
  78. }
  79. try {
  80. const { html } = await response.json();
  81. if (!html) {
  82. throw new Error("Generation failed (invalid response)");
  83. }
  84. return {
  85. html,
  86. };
  87. } catch (error: any) {
  88. throw new Error("Generation failed (invalid response)");
  89. }
  90. }}
  91. />
  92. <TTDDialog
  93. onTextSubmit={async (input) => {
  94. try {
  95. const response = await fetch(
  96. `${
  97. import.meta.env.VITE_APP_AI_BACKEND
  98. }/v1/ai/text-to-diagram/generate`,
  99. {
  100. method: "POST",
  101. headers: {
  102. Accept: "application/json",
  103. "Content-Type": "application/json",
  104. },
  105. body: JSON.stringify({ prompt: input }),
  106. },
  107. );
  108. const rateLimit = response.headers.has("X-Ratelimit-Limit")
  109. ? parseInt(response.headers.get("X-Ratelimit-Limit") || "0", 10)
  110. : undefined;
  111. const rateLimitRemaining = response.headers.has(
  112. "X-Ratelimit-Remaining",
  113. )
  114. ? parseInt(
  115. response.headers.get("X-Ratelimit-Remaining") || "0",
  116. 10,
  117. )
  118. : undefined;
  119. const json = await response.json();
  120. if (!response.ok) {
  121. if (response.status === 429) {
  122. return {
  123. rateLimit,
  124. rateLimitRemaining,
  125. error: new Error(
  126. "Too many requests today, please try again tomorrow!",
  127. ),
  128. };
  129. }
  130. throw new Error(json.message || "Generation failed...");
  131. }
  132. const generatedResponse = json.generatedResponse;
  133. if (!generatedResponse) {
  134. throw new Error("Generation failed...");
  135. }
  136. return { generatedResponse, rateLimit, rateLimitRemaining };
  137. } catch (err: any) {
  138. throw new Error("Request failed");
  139. }
  140. }}
  141. />
  142. </>
  143. );
  144. };