@comergehq/studio 0.1.31 → 0.1.32
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/dist/index.js +120 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +120 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/data/agent/remote.ts +8 -0
- package/src/data/agent/repository.ts +8 -0
- package/src/data/agent/types.ts +16 -0
- package/src/data/attachment/remote.ts +15 -0
- package/src/data/attachment/repository.ts +21 -0
- package/src/data/attachment/types.ts +20 -0
- package/src/studio/ComergeStudio.tsx +1 -0
- package/src/studio/hooks/useAttachmentUpload.ts +38 -1
- package/src/studio/hooks/useOptimisticChatMessages.ts +5 -5
- package/src/studio/hooks/useStudioActions.ts +50 -7
- package/src/studio/ui/ChatPanel.tsx +6 -6
package/dist/index.js
CHANGED
|
@@ -2899,6 +2899,13 @@ var AttachmentRemoteDataSourceImpl = class extends BaseRemote {
|
|
|
2899
2899
|
);
|
|
2900
2900
|
return data;
|
|
2901
2901
|
}
|
|
2902
|
+
async stagePresign(payload) {
|
|
2903
|
+
const { data } = await api.post(
|
|
2904
|
+
"/v1/attachments/stage/presign",
|
|
2905
|
+
payload
|
|
2906
|
+
);
|
|
2907
|
+
return data;
|
|
2908
|
+
}
|
|
2902
2909
|
};
|
|
2903
2910
|
var attachmentRemoteDataSource = new AttachmentRemoteDataSourceImpl();
|
|
2904
2911
|
|
|
@@ -2912,6 +2919,10 @@ var AttachmentRepositoryImpl = class extends BaseRepository {
|
|
|
2912
2919
|
const res = await this.remote.presign(payload);
|
|
2913
2920
|
return this.unwrapOrThrow(res);
|
|
2914
2921
|
}
|
|
2922
|
+
async stagePresign(payload) {
|
|
2923
|
+
const res = await this.remote.stagePresign(payload);
|
|
2924
|
+
return this.unwrapOrThrow(res);
|
|
2925
|
+
}
|
|
2915
2926
|
async upload(upload, file) {
|
|
2916
2927
|
const resp = await fetch(upload.uploadUrl, {
|
|
2917
2928
|
method: "PUT",
|
|
@@ -2922,6 +2933,16 @@ var AttachmentRepositoryImpl = class extends BaseRepository {
|
|
|
2922
2933
|
throw new Error(`upload failed: ${resp.status}`);
|
|
2923
2934
|
}
|
|
2924
2935
|
}
|
|
2936
|
+
async uploadStaged(upload, file) {
|
|
2937
|
+
const resp = await fetch(upload.uploadUrl, {
|
|
2938
|
+
method: "PUT",
|
|
2939
|
+
headers: upload.headers,
|
|
2940
|
+
body: file
|
|
2941
|
+
});
|
|
2942
|
+
if (!resp.ok) {
|
|
2943
|
+
throw new Error(`staged upload failed: ${resp.status}`);
|
|
2944
|
+
}
|
|
2945
|
+
}
|
|
2925
2946
|
};
|
|
2926
2947
|
var attachmentRepository = new AttachmentRepositoryImpl(
|
|
2927
2948
|
attachmentRemoteDataSource
|
|
@@ -2995,7 +3016,36 @@ function useAttachmentUpload() {
|
|
|
2995
3016
|
setUploading(false);
|
|
2996
3017
|
}
|
|
2997
3018
|
}, []);
|
|
2998
|
-
|
|
3019
|
+
const stageBase64Images = React7.useCallback(async ({ dataUrls }) => {
|
|
3020
|
+
if (!dataUrls || dataUrls.length === 0) return [];
|
|
3021
|
+
setUploading(true);
|
|
3022
|
+
setError(null);
|
|
3023
|
+
try {
|
|
3024
|
+
const blobs = await Promise.all(
|
|
3025
|
+
dataUrls.map(async (dataUrl) => {
|
|
3026
|
+
const normalized = dataUrl.startsWith("data:") ? dataUrl : `data:image/png;base64,${dataUrl}`;
|
|
3027
|
+
const blob = import_react_native6.Platform.OS === "android" ? await dataUrlToBlobAndroid(normalized) : await (await fetch(normalized)).blob();
|
|
3028
|
+
const mimeType = getMimeTypeFromDataUrl(normalized);
|
|
3029
|
+
return { blob, mimeType };
|
|
3030
|
+
})
|
|
3031
|
+
);
|
|
3032
|
+
const files = blobs.map(({ blob, mimeType }, idx) => ({
|
|
3033
|
+
name: `attachment-${Date.now()}-${idx}.png`,
|
|
3034
|
+
size: blob.size,
|
|
3035
|
+
mimeType
|
|
3036
|
+
}));
|
|
3037
|
+
const presign = await attachmentRepository.stagePresign({ files });
|
|
3038
|
+
await Promise.all(presign.uploads.map((u, index) => attachmentRepository.uploadStaged(u, blobs[index].blob)));
|
|
3039
|
+
return presign.uploads.map((u) => u.attachmentToken);
|
|
3040
|
+
} catch (e) {
|
|
3041
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
3042
|
+
setError(err);
|
|
3043
|
+
throw err;
|
|
3044
|
+
} finally {
|
|
3045
|
+
setUploading(false);
|
|
3046
|
+
}
|
|
3047
|
+
}, []);
|
|
3048
|
+
return { uploadBase64Images, stageBase64Images, uploading, error };
|
|
2999
3049
|
}
|
|
3000
3050
|
|
|
3001
3051
|
// src/studio/hooks/useStudioActions.ts
|
|
@@ -3011,6 +3061,10 @@ var AgentRemoteDataSourceImpl = class extends BaseRemote {
|
|
|
3011
3061
|
const { data } = await api.post("/v1/agent/editApp", payload);
|
|
3012
3062
|
return data;
|
|
3013
3063
|
}
|
|
3064
|
+
async forkEditStart(payload) {
|
|
3065
|
+
const { data } = await api.post("/v1/agent/forkEditStart", payload);
|
|
3066
|
+
return data;
|
|
3067
|
+
}
|
|
3014
3068
|
};
|
|
3015
3069
|
var agentRemoteDataSource = new AgentRemoteDataSourceImpl();
|
|
3016
3070
|
|
|
@@ -3028,6 +3082,10 @@ var AgentRepositoryImpl = class extends BaseRepository {
|
|
|
3028
3082
|
const res = await this.remote.editApp(payload);
|
|
3029
3083
|
return this.unwrapOrThrow(res);
|
|
3030
3084
|
}
|
|
3085
|
+
async forkEditStart(payload) {
|
|
3086
|
+
const res = await this.remote.forkEditStart(payload);
|
|
3087
|
+
return this.unwrapOrThrow(res);
|
|
3088
|
+
}
|
|
3031
3089
|
};
|
|
3032
3090
|
var agentRepository = new AgentRepositoryImpl(agentRemoteDataSource);
|
|
3033
3091
|
|
|
@@ -3078,7 +3136,8 @@ function useStudioActions({
|
|
|
3078
3136
|
onEditStart,
|
|
3079
3137
|
onEditQueued,
|
|
3080
3138
|
onEditFinished,
|
|
3081
|
-
uploadAttachments
|
|
3139
|
+
uploadAttachments,
|
|
3140
|
+
stageAttachments
|
|
3082
3141
|
}) {
|
|
3083
3142
|
const [forking, setForking] = React8.useState(false);
|
|
3084
3143
|
const [sending, setSending] = React8.useState(false);
|
|
@@ -3098,16 +3157,46 @@ function useStudioActions({
|
|
|
3098
3157
|
const sourceAppId = app.id;
|
|
3099
3158
|
if (shouldForkOnEdit) {
|
|
3100
3159
|
setForking(true);
|
|
3101
|
-
|
|
3102
|
-
|
|
3160
|
+
let attachmentTokens;
|
|
3161
|
+
if (attachments && attachments.length > 0 && stageAttachments) {
|
|
3162
|
+
attachmentTokens = await stageAttachments({ dataUrls: attachments });
|
|
3163
|
+
}
|
|
3164
|
+
const idempotencyKey2 = generateIdempotencyKey();
|
|
3165
|
+
const startResult = await withRetry2(
|
|
3166
|
+
async () => await agentRepository.forkEditStart({
|
|
3167
|
+
source_app_id: sourceAppId,
|
|
3168
|
+
prompt,
|
|
3169
|
+
attachmentTokens,
|
|
3170
|
+
idempotencyKey: idempotencyKey2
|
|
3171
|
+
}),
|
|
3172
|
+
{ attempts: 3, baseDelayMs: 500, maxDelayMs: 4e3 }
|
|
3173
|
+
);
|
|
3174
|
+
targetApp = {
|
|
3175
|
+
...app,
|
|
3176
|
+
id: startResult.targetAppId,
|
|
3177
|
+
threadId: startResult.targetThreadId
|
|
3178
|
+
};
|
|
3103
3179
|
await trackRemixApp({
|
|
3104
|
-
appId:
|
|
3180
|
+
appId: startResult.targetAppId,
|
|
3105
3181
|
sourceAppId,
|
|
3106
|
-
threadId:
|
|
3182
|
+
threadId: startResult.targetThreadId ?? void 0,
|
|
3107
3183
|
success: true
|
|
3108
3184
|
});
|
|
3109
3185
|
forkSucceeded = true;
|
|
3110
|
-
onForkedApp == null ? void 0 : onForkedApp(
|
|
3186
|
+
onForkedApp == null ? void 0 : onForkedApp(startResult.targetAppId, { keepRenderingAppId: sourceAppId });
|
|
3187
|
+
onEditStart == null ? void 0 : onEditStart();
|
|
3188
|
+
onEditQueued == null ? void 0 : onEditQueued({
|
|
3189
|
+
queueItemId: startResult.queueItemId ?? null,
|
|
3190
|
+
queuePosition: startResult.queuePosition ?? null
|
|
3191
|
+
});
|
|
3192
|
+
await trackEditApp({
|
|
3193
|
+
appId: startResult.targetAppId,
|
|
3194
|
+
threadId: startResult.targetThreadId,
|
|
3195
|
+
promptLength: prompt.trim().length,
|
|
3196
|
+
success: true
|
|
3197
|
+
});
|
|
3198
|
+
setForking(false);
|
|
3199
|
+
return;
|
|
3111
3200
|
}
|
|
3112
3201
|
setForking(false);
|
|
3113
3202
|
const threadId = targetApp.threadId;
|
|
@@ -3168,7 +3257,18 @@ function useStudioActions({
|
|
|
3168
3257
|
onEditFinished == null ? void 0 : onEditFinished();
|
|
3169
3258
|
}
|
|
3170
3259
|
},
|
|
3171
|
-
[
|
|
3260
|
+
[
|
|
3261
|
+
app,
|
|
3262
|
+
onEditFinished,
|
|
3263
|
+
onEditQueued,
|
|
3264
|
+
onEditStart,
|
|
3265
|
+
onForkedApp,
|
|
3266
|
+
sending,
|
|
3267
|
+
shouldForkOnEdit,
|
|
3268
|
+
stageAttachments,
|
|
3269
|
+
uploadAttachments,
|
|
3270
|
+
userId
|
|
3271
|
+
]
|
|
3172
3272
|
);
|
|
3173
3273
|
return { isOwner, shouldForkOnEdit, forking, sending, error, sendEdit };
|
|
3174
3274
|
}
|
|
@@ -8396,7 +8496,7 @@ function ChatPanel({
|
|
|
8396
8496
|
showTypingIndicator,
|
|
8397
8497
|
loading,
|
|
8398
8498
|
sendDisabled,
|
|
8399
|
-
forking = false,
|
|
8499
|
+
forking: _forking = false,
|
|
8400
8500
|
sending,
|
|
8401
8501
|
shouldForkOnEdit,
|
|
8402
8502
|
attachments = [],
|
|
@@ -8455,7 +8555,7 @@ function ChatPanel({
|
|
|
8455
8555
|
style: { marginBottom: 12 }
|
|
8456
8556
|
}
|
|
8457
8557
|
) : null;
|
|
8458
|
-
const showMessagesLoading = Boolean(loading) && messages.length === 0
|
|
8558
|
+
const showMessagesLoading = Boolean(loading) && messages.length === 0;
|
|
8459
8559
|
if (showMessagesLoading) {
|
|
8460
8560
|
return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_react_native58.View, { style: { flex: 1 }, children: [
|
|
8461
8561
|
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_react_native58.View, { children: header }),
|
|
@@ -8463,14 +8563,14 @@ function ChatPanel({
|
|
|
8463
8563
|
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_react_native58.View, { style: { flex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 24, paddingVertical: 12 }, children: [
|
|
8464
8564
|
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_react_native58.ActivityIndicator, {}),
|
|
8465
8565
|
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_react_native58.View, { style: { height: 12 } }),
|
|
8466
|
-
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Text, { variant: "bodyMuted", children:
|
|
8566
|
+
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Text, { variant: "bodyMuted", children: "Loading messages\u2026" })
|
|
8467
8567
|
] })
|
|
8468
8568
|
] });
|
|
8469
8569
|
}
|
|
8470
8570
|
const bundleProgress = (progress == null ? void 0 : progress.bundle) ?? null;
|
|
8471
8571
|
const queueTop = progress || queueItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_react_native58.View, { style: { gap: theme.spacing.sm }, children: [
|
|
8472
8572
|
progress ? bundleProgress ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(BundleProgressCard, { progress: bundleProgress }) : /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(AgentProgressCard, { progress }) : null,
|
|
8473
|
-
queueItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(ChatQueue, { items: queueItems, onRemove: onRemoveQueueItem }) : null
|
|
8573
|
+
!progress && queueItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(ChatQueue, { items: queueItems, onRemove: onRemoveQueueItem }) : null
|
|
8474
8574
|
] }) : null;
|
|
8475
8575
|
return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
|
|
8476
8576
|
ChatPage,
|
|
@@ -8497,8 +8597,8 @@ function ChatPanel({
|
|
|
8497
8597
|
composer: {
|
|
8498
8598
|
// Keep the input editable even when sending is disallowed (e.g. agent still working),
|
|
8499
8599
|
// otherwise iOS will drop focus/keyboard and BottomSheet can get "stuck" with a keyboard-sized gap.
|
|
8500
|
-
disabled: Boolean(loading)
|
|
8501
|
-
sendDisabled: Boolean(sendDisabled) || Boolean(loading)
|
|
8600
|
+
disabled: Boolean(loading),
|
|
8601
|
+
sendDisabled: Boolean(sendDisabled) || Boolean(loading),
|
|
8502
8602
|
sending: Boolean(sending),
|
|
8503
8603
|
onSend: handleSend,
|
|
8504
8604
|
attachments,
|
|
@@ -8714,7 +8814,7 @@ function isOptimisticResolvedByServer(chatMessages, o) {
|
|
|
8714
8814
|
}
|
|
8715
8815
|
function useOptimisticChatMessages({
|
|
8716
8816
|
threadId,
|
|
8717
|
-
shouldForkOnEdit,
|
|
8817
|
+
shouldForkOnEdit: _shouldForkOnEdit,
|
|
8718
8818
|
disableOptimistic = false,
|
|
8719
8819
|
chatMessages,
|
|
8720
8820
|
onSendChat
|
|
@@ -8749,7 +8849,7 @@ function useOptimisticChatMessages({
|
|
|
8749
8849
|
}, [chatMessages, optimisticChat.length]);
|
|
8750
8850
|
const onSend = React45.useCallback(
|
|
8751
8851
|
async (text, attachments) => {
|
|
8752
|
-
if (
|
|
8852
|
+
if (disableOptimistic) {
|
|
8753
8853
|
await onSendChat(text, attachments);
|
|
8754
8854
|
return;
|
|
8755
8855
|
}
|
|
@@ -8773,11 +8873,11 @@ function useOptimisticChatMessages({
|
|
|
8773
8873
|
setOptimisticChat((prev) => prev.map((m) => m.id === id ? { ...m, failed: true } : m));
|
|
8774
8874
|
});
|
|
8775
8875
|
},
|
|
8776
|
-
[chatMessages, disableOptimistic, onSendChat
|
|
8876
|
+
[chatMessages, disableOptimistic, onSendChat]
|
|
8777
8877
|
);
|
|
8778
8878
|
const onRetry = React45.useCallback(
|
|
8779
8879
|
async (messageId) => {
|
|
8780
|
-
if (
|
|
8880
|
+
if (disableOptimistic) return;
|
|
8781
8881
|
const target = optimisticChat.find((m) => m.id === messageId);
|
|
8782
8882
|
if (!target || target.retrying) return;
|
|
8783
8883
|
const baseServerLastId = chatMessages.length > 0 ? chatMessages[chatMessages.length - 1].id : null;
|
|
@@ -8797,7 +8897,7 @@ function useOptimisticChatMessages({
|
|
|
8797
8897
|
);
|
|
8798
8898
|
}
|
|
8799
8899
|
},
|
|
8800
|
-
[chatMessages, disableOptimistic, onSendChat, optimisticChat
|
|
8900
|
+
[chatMessages, disableOptimistic, onSendChat, optimisticChat]
|
|
8801
8901
|
);
|
|
8802
8902
|
const isRetrying = React45.useCallback(
|
|
8803
8903
|
(messageId) => {
|
|
@@ -9950,6 +10050,7 @@ function ComergeStudioInner({
|
|
|
9950
10050
|
}
|
|
9951
10051
|
},
|
|
9952
10052
|
uploadAttachments: uploader.uploadBase64Images,
|
|
10053
|
+
stageAttachments: uploader.stageBase64Images,
|
|
9953
10054
|
onEditStart: () => {
|
|
9954
10055
|
if (editQueue.items.length === 0) {
|
|
9955
10056
|
setSuppressQueueUntilResponse(true);
|