@animaapp/anima-sdk-react 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +7 -7
- package/dist/index.cjs +6 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +1404 -1343
- package/dist/index.js.map +1 -1
- package/package.json +3 -4
- package/src/useAnimaCodegen.ts +123 -22
- package/src/utils.ts +10 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@animaapp/anima-sdk-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Anima's JavaScript utilities library",
|
|
6
6
|
"author": "Anima App, Inc.",
|
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
"url": "git+https://github.com/AnimaApp/anima-sdk.git"
|
|
17
17
|
},
|
|
18
18
|
"publishConfig": {
|
|
19
|
-
"access": "public"
|
|
20
|
-
"registry": "https://registry.npmjs.org/"
|
|
19
|
+
"access": "public"
|
|
21
20
|
},
|
|
22
21
|
"scripts": {
|
|
23
22
|
"build": "vite build",
|
|
@@ -41,4 +40,4 @@
|
|
|
41
40
|
"vite-tsconfig-paths": "^5.1.4",
|
|
42
41
|
"vitest": "^3.0.5"
|
|
43
42
|
}
|
|
44
|
-
}
|
|
43
|
+
}
|
package/src/useAnimaCodegen.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { arrayBufferToBase64 } from "./utils";
|
|
1
2
|
import type {
|
|
2
3
|
AnimaSDKResult,
|
|
3
4
|
GetCodeParams,
|
|
@@ -7,6 +8,14 @@ import { convertCodegenFilesToAnimaFiles } from "@animaapp/anima-sdk";
|
|
|
7
8
|
import { EventSource } from "eventsource";
|
|
8
9
|
import { useImmer } from "use-immer";
|
|
9
10
|
|
|
11
|
+
type LocalAssetsStorage =
|
|
12
|
+
| { strategy: "local"; path: string }
|
|
13
|
+
| { strategy: "local"; filePath: string; referencePath: string };
|
|
14
|
+
|
|
15
|
+
export type UseAnimaParams = Omit<GetCodeParams, "assetsStorage"> & {
|
|
16
|
+
assetsStorage?: GetCodeParams["assetsStorage"] | LocalAssetsStorage;
|
|
17
|
+
};
|
|
18
|
+
|
|
10
19
|
type Status = "idle" | "pending" | "success" | "aborted" | "error";
|
|
11
20
|
|
|
12
21
|
type TaskStatus = "pending" | "running" | "finished";
|
|
@@ -33,7 +42,30 @@ const defaultProgress: CodegenStatus = {
|
|
|
33
42
|
},
|
|
34
43
|
};
|
|
35
44
|
|
|
36
|
-
type StreamMessageByType<T extends StreamCodgenMessage[
|
|
45
|
+
type StreamMessageByType<T extends StreamCodgenMessage["type"]> = Extract<
|
|
46
|
+
StreamCodgenMessage,
|
|
47
|
+
{ type: T }
|
|
48
|
+
>;
|
|
49
|
+
|
|
50
|
+
const getAssetsLocalStrategyParams = (
|
|
51
|
+
localAssetsStorage: LocalAssetsStorage
|
|
52
|
+
) => {
|
|
53
|
+
if ("path" in localAssetsStorage) {
|
|
54
|
+
return {
|
|
55
|
+
filePath: localAssetsStorage.path.replace(/^\//, ""),
|
|
56
|
+
referencePath:
|
|
57
|
+
localAssetsStorage.path === "/" ? "" : localAssetsStorage.path, // Workaround to avoid duplicated slashes in the URL. Ideally, the fix should be done in Codegen.
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
filePath: localAssetsStorage.filePath.replace(/^\//, ""),
|
|
63
|
+
referencePath:
|
|
64
|
+
localAssetsStorage.referencePath === "/"
|
|
65
|
+
? ""
|
|
66
|
+
: localAssetsStorage.referencePath,
|
|
67
|
+
};
|
|
68
|
+
};
|
|
37
69
|
|
|
38
70
|
export const useAnimaCodegen = ({
|
|
39
71
|
url,
|
|
@@ -44,7 +76,9 @@ export const useAnimaCodegen = ({
|
|
|
44
76
|
}) => {
|
|
45
77
|
const [status, updateStatus] = useImmer<CodegenStatus>(defaultProgress);
|
|
46
78
|
|
|
47
|
-
const getCode = async <T =
|
|
79
|
+
const getCode = async <T extends UseAnimaParams = UseAnimaParams>(
|
|
80
|
+
params: T
|
|
81
|
+
) => {
|
|
48
82
|
updateStatus((draft) => {
|
|
49
83
|
draft.status = "pending";
|
|
50
84
|
draft.error = null;
|
|
@@ -52,6 +86,19 @@ export const useAnimaCodegen = ({
|
|
|
52
86
|
draft.tasks = defaultProgress.tasks;
|
|
53
87
|
});
|
|
54
88
|
|
|
89
|
+
const initialParams = structuredClone(params);
|
|
90
|
+
|
|
91
|
+
if (params.assetsStorage?.strategy === "local") {
|
|
92
|
+
const { referencePath } = getAssetsLocalStrategyParams(
|
|
93
|
+
params.assetsStorage
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
params.assetsStorage = {
|
|
97
|
+
strategy: "external",
|
|
98
|
+
url: referencePath,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
55
102
|
const es = new EventSource(url, {
|
|
56
103
|
fetch: (url, init) =>
|
|
57
104
|
fetch(url, {
|
|
@@ -65,12 +112,11 @@ export const useAnimaCodegen = ({
|
|
|
65
112
|
result: AnimaSDKResult | null;
|
|
66
113
|
error: Error | null;
|
|
67
114
|
}>((resolve) => {
|
|
68
|
-
|
|
69
115
|
const result: Partial<AnimaSDKResult> = {};
|
|
70
116
|
|
|
71
117
|
// Add specific event listeners
|
|
72
|
-
es.addEventListener(
|
|
73
|
-
const message = JSON.parse(event.data) as StreamMessageByType<
|
|
118
|
+
es.addEventListener("start", (event) => {
|
|
119
|
+
const message = JSON.parse(event.data) as StreamMessageByType<"start">;
|
|
74
120
|
result.sessionId = message.sessionId;
|
|
75
121
|
|
|
76
122
|
updateStatus((draft) => {
|
|
@@ -78,8 +124,10 @@ export const useAnimaCodegen = ({
|
|
|
78
124
|
});
|
|
79
125
|
});
|
|
80
126
|
|
|
81
|
-
es.addEventListener(
|
|
82
|
-
const message = JSON.parse(
|
|
127
|
+
es.addEventListener("pre_codegen", (event) => {
|
|
128
|
+
const message = JSON.parse(
|
|
129
|
+
event.data
|
|
130
|
+
) as StreamMessageByType<"pre_codegen">;
|
|
83
131
|
if (message.message === "Anima model built") {
|
|
84
132
|
updateStatus((draft) => {
|
|
85
133
|
draft.tasks.fetchDesign.status = "finished";
|
|
@@ -89,13 +137,15 @@ export const useAnimaCodegen = ({
|
|
|
89
137
|
}
|
|
90
138
|
});
|
|
91
139
|
|
|
92
|
-
es.addEventListener(
|
|
93
|
-
const message = JSON.parse(
|
|
140
|
+
es.addEventListener("figma_metadata", (e) => {
|
|
141
|
+
const message = JSON.parse(
|
|
142
|
+
e.data
|
|
143
|
+
) as StreamMessageByType<"figma_metadata">;
|
|
94
144
|
result.figmaFileName = message.figmaFileName;
|
|
95
145
|
result.figmaSelectedFrameName = message.figmaSelectedFrameName;
|
|
96
146
|
});
|
|
97
147
|
|
|
98
|
-
es.addEventListener(
|
|
148
|
+
es.addEventListener("aborted", () => {
|
|
99
149
|
updateStatus((draft) => {
|
|
100
150
|
draft.status = "aborted";
|
|
101
151
|
});
|
|
@@ -105,8 +155,10 @@ export const useAnimaCodegen = ({
|
|
|
105
155
|
});
|
|
106
156
|
});
|
|
107
157
|
|
|
108
|
-
es.addEventListener(
|
|
109
|
-
const message = JSON.parse(
|
|
158
|
+
es.addEventListener("generating_code", (event) => {
|
|
159
|
+
const message = JSON.parse(
|
|
160
|
+
event.data
|
|
161
|
+
) as StreamMessageByType<"generating_code">;
|
|
110
162
|
if (message.payload.status === "success") {
|
|
111
163
|
const codegenFiles = message.payload.files as Record<
|
|
112
164
|
string,
|
|
@@ -121,22 +173,33 @@ export const useAnimaCodegen = ({
|
|
|
121
173
|
});
|
|
122
174
|
});
|
|
123
175
|
|
|
124
|
-
es.addEventListener(
|
|
176
|
+
es.addEventListener("codegen_completed", () => {
|
|
125
177
|
updateStatus((draft) => {
|
|
126
178
|
draft.tasks.codeGeneration.status = "finished";
|
|
127
179
|
});
|
|
128
180
|
});
|
|
129
181
|
|
|
130
|
-
es.addEventListener(
|
|
182
|
+
es.addEventListener("assets_uploaded", () => {
|
|
131
183
|
updateStatus((draft) => {
|
|
132
184
|
draft.tasks.uploadAssets.status = "finished";
|
|
133
185
|
});
|
|
134
186
|
});
|
|
135
187
|
|
|
136
|
-
es.addEventListener(
|
|
188
|
+
es.addEventListener("assets_list", (event) => {
|
|
189
|
+
const message = JSON.parse(
|
|
190
|
+
event.data
|
|
191
|
+
) as StreamMessageByType<"assets_list">;
|
|
192
|
+
|
|
193
|
+
result.assets = message.payload.assets;
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
// TODO: For some reason, we receive errors even after the `done` event is triggered.
|
|
197
|
+
es.addEventListener("error", (error: ErrorEvent | MessageEvent) => {
|
|
137
198
|
// Differentiate between an error message from the server and an error event from the EventSource
|
|
138
|
-
if (
|
|
139
|
-
const message = JSON.parse(
|
|
199
|
+
if (error instanceof MessageEvent) {
|
|
200
|
+
const message = JSON.parse(
|
|
201
|
+
error.data
|
|
202
|
+
) as StreamMessageByType<"error">;
|
|
140
203
|
updateStatus((draft) => {
|
|
141
204
|
draft.status = "error";
|
|
142
205
|
draft.error = new Error(message.payload.message);
|
|
@@ -148,21 +211,21 @@ export const useAnimaCodegen = ({
|
|
|
148
211
|
});
|
|
149
212
|
} else {
|
|
150
213
|
// It's an EventSource error (e.g. HTTP error)
|
|
151
|
-
console.error(
|
|
214
|
+
console.error("EventSource error:", error);
|
|
152
215
|
|
|
153
216
|
updateStatus((draft) => {
|
|
154
217
|
draft.status = "error";
|
|
155
|
-
draft.error = new Error("HTTP error: " +
|
|
218
|
+
draft.error = new Error("HTTP error: " + error.message);
|
|
156
219
|
});
|
|
157
220
|
|
|
158
221
|
resolve({
|
|
159
222
|
result: null,
|
|
160
|
-
error: new Error("HTTP error: " +
|
|
223
|
+
error: new Error("HTTP error: " + error.message),
|
|
161
224
|
});
|
|
162
225
|
}
|
|
163
226
|
});
|
|
164
227
|
|
|
165
|
-
es.addEventListener(
|
|
228
|
+
es.addEventListener("done", () => {
|
|
166
229
|
updateStatus((draft) => {
|
|
167
230
|
draft.status = "success";
|
|
168
231
|
draft.result = result as AnimaSDKResult;
|
|
@@ -173,7 +236,45 @@ export const useAnimaCodegen = ({
|
|
|
173
236
|
});
|
|
174
237
|
|
|
175
238
|
try {
|
|
176
|
-
const { result, error } = await promise;
|
|
239
|
+
const { result: r, error } = await promise;
|
|
240
|
+
|
|
241
|
+
const result = structuredClone(r);
|
|
242
|
+
|
|
243
|
+
// Ideally, we should download the assets within the `assets_uploaded` event handler, since it'll improve the performance.
|
|
244
|
+
// But for some reason, it doesn't work. So, we download the assets here.
|
|
245
|
+
if (
|
|
246
|
+
initialParams.assetsStorage?.strategy === "local" &&
|
|
247
|
+
result?.assets?.length
|
|
248
|
+
) {
|
|
249
|
+
const { filePath } = getAssetsLocalStrategyParams(
|
|
250
|
+
initialParams.assetsStorage
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
const downloadAssetsPromises = result.assets.map(async (asset) => {
|
|
254
|
+
const response = await fetch(asset.url);
|
|
255
|
+
const buffer = await response.arrayBuffer();
|
|
256
|
+
return {
|
|
257
|
+
assetName: asset.name,
|
|
258
|
+
base64: arrayBufferToBase64(buffer),
|
|
259
|
+
};
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
const assets = await Promise.allSettled(downloadAssetsPromises);
|
|
263
|
+
for (const assetPromise of assets) {
|
|
264
|
+
const assetsList: Record<string, string> = {};
|
|
265
|
+
if (assetPromise.status === "fulfilled") {
|
|
266
|
+
const { assetName, base64 } = assetPromise.value;
|
|
267
|
+
|
|
268
|
+
assetsList[assetName] = base64;
|
|
269
|
+
|
|
270
|
+
const assetPath = filePath ? `${filePath}/${assetName}` : assetName;
|
|
271
|
+
result.files[assetPath] = {
|
|
272
|
+
content: base64,
|
|
273
|
+
isBinary: true,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
177
278
|
|
|
178
279
|
if (error) {
|
|
179
280
|
return { result: null, error };
|
package/src/utils.ts
ADDED