@comergehq/studio 0.1.15 → 0.1.17
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 +615 -177
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +609 -171
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/ChatPage.tsx +19 -2
- package/src/components/chat/ChatQueue.tsx +163 -0
- package/src/data/agent/types.ts +2 -1
- package/src/data/apps/edit-queue/remote.ts +45 -0
- package/src/data/apps/edit-queue/repository.ts +136 -0
- package/src/data/apps/edit-queue/types.ts +31 -0
- package/src/studio/ComergeStudio.tsx +70 -2
- package/src/studio/hooks/useEditQueue.ts +71 -0
- package/src/studio/hooks/useEditQueueActions.ts +29 -0
- package/src/studio/hooks/useOptimisticChatMessages.ts +4 -2
- package/src/studio/hooks/useStudioActions.ts +14 -2
- package/src/studio/hooks/useThreadMessages.ts +43 -6
- package/src/studio/ui/ChatPanel.tsx +11 -0
- package/src/studio/ui/StudioOverlay.tsx +10 -0
package/dist/index.js
CHANGED
|
@@ -36,8 +36,8 @@ __export(index_exports, {
|
|
|
36
36
|
module.exports = __toCommonJS(index_exports);
|
|
37
37
|
|
|
38
38
|
// src/studio/ComergeStudio.tsx
|
|
39
|
-
var
|
|
40
|
-
var
|
|
39
|
+
var React46 = __toESM(require("react"));
|
|
40
|
+
var import_react_native56 = require("react-native");
|
|
41
41
|
var import_bottom_sheet6 = require("@gorhom/bottom-sheet");
|
|
42
42
|
|
|
43
43
|
// src/studio/bootstrap/StudioBootstrap.tsx
|
|
@@ -846,6 +846,35 @@ function extractMeta(payload) {
|
|
|
846
846
|
threadId: typeof obj.threadId === "string" ? obj.threadId : void 0
|
|
847
847
|
};
|
|
848
848
|
}
|
|
849
|
+
function getPayloadMeta(payload) {
|
|
850
|
+
const meta = payload == null ? void 0 : payload.meta;
|
|
851
|
+
if (!meta || typeof meta !== "object") return null;
|
|
852
|
+
return meta;
|
|
853
|
+
}
|
|
854
|
+
function isQueuedHiddenMessage(m) {
|
|
855
|
+
if (m.authorType !== "human") return false;
|
|
856
|
+
const meta = getPayloadMeta(m.payload);
|
|
857
|
+
return (meta == null ? void 0 : meta.visibility) === "queued";
|
|
858
|
+
}
|
|
859
|
+
function toEpochMs(value) {
|
|
860
|
+
if (value == null) return 0;
|
|
861
|
+
if (typeof value === "number") return value;
|
|
862
|
+
if (value instanceof Date) return value.getTime();
|
|
863
|
+
const parsed = Date.parse(String(value));
|
|
864
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
865
|
+
}
|
|
866
|
+
function getEffectiveSortMs(m) {
|
|
867
|
+
const meta = getPayloadMeta(m.payload);
|
|
868
|
+
const runStartedAt = meta == null ? void 0 : meta.runStartedAt;
|
|
869
|
+
const runMs = toEpochMs(runStartedAt);
|
|
870
|
+
return runMs > 0 ? runMs : toEpochMs(m.createdAt);
|
|
871
|
+
}
|
|
872
|
+
function compareMessages(a, b) {
|
|
873
|
+
const aMs = getEffectiveSortMs(a);
|
|
874
|
+
const bMs = getEffectiveSortMs(b);
|
|
875
|
+
if (aMs !== bMs) return aMs - bMs;
|
|
876
|
+
return String(a.createdAt).localeCompare(String(b.createdAt));
|
|
877
|
+
}
|
|
849
878
|
function mapMessageToChatMessage(m) {
|
|
850
879
|
var _a, _b;
|
|
851
880
|
const kind = typeof ((_a = m.payload) == null ? void 0 : _a.type) === "string" ? String(m.payload.type) : null;
|
|
@@ -865,8 +894,9 @@ function useThreadMessages(threadId) {
|
|
|
865
894
|
const activeRequestIdRef = React4.useRef(0);
|
|
866
895
|
const foregroundSignal = useForegroundSignal(Boolean(threadId));
|
|
867
896
|
const upsertSorted = React4.useCallback((prev, m) => {
|
|
868
|
-
const next = prev.
|
|
869
|
-
next.
|
|
897
|
+
const next = prev.filter((x) => x.id !== m.id);
|
|
898
|
+
next.push(m);
|
|
899
|
+
next.sort(compareMessages);
|
|
870
900
|
return next;
|
|
871
901
|
}, []);
|
|
872
902
|
const refetch = React4.useCallback(async () => {
|
|
@@ -880,7 +910,7 @@ function useThreadMessages(threadId) {
|
|
|
880
910
|
try {
|
|
881
911
|
const list = await messagesRepository.list(threadId);
|
|
882
912
|
if (activeRequestIdRef.current !== requestId) return;
|
|
883
|
-
setRaw([...list].sort(
|
|
913
|
+
setRaw([...list].sort(compareMessages));
|
|
884
914
|
} catch (e) {
|
|
885
915
|
if (activeRequestIdRef.current !== requestId) return;
|
|
886
916
|
setError(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -906,7 +936,11 @@ function useThreadMessages(threadId) {
|
|
|
906
936
|
if (foregroundSignal <= 0) return;
|
|
907
937
|
void refetch();
|
|
908
938
|
}, [foregroundSignal, refetch, threadId]);
|
|
909
|
-
const messages = React4.useMemo(() =>
|
|
939
|
+
const messages = React4.useMemo(() => {
|
|
940
|
+
const visible = raw.filter((m) => !isQueuedHiddenMessage(m));
|
|
941
|
+
const resolved = visible.length > 0 ? visible : raw;
|
|
942
|
+
return resolved.map(mapMessageToChatMessage);
|
|
943
|
+
}, [raw]);
|
|
910
944
|
return { raw, messages, loading, error, refetch };
|
|
911
945
|
}
|
|
912
946
|
|
|
@@ -1932,6 +1966,9 @@ function useStudioActions({
|
|
|
1932
1966
|
userId,
|
|
1933
1967
|
app,
|
|
1934
1968
|
onForkedApp,
|
|
1969
|
+
onEditStart,
|
|
1970
|
+
onEditQueued,
|
|
1971
|
+
onEditFinished,
|
|
1935
1972
|
uploadAttachments
|
|
1936
1973
|
}) {
|
|
1937
1974
|
const [forking, setForking] = React8.useState(false);
|
|
@@ -1958,16 +1995,21 @@ function useStudioActions({
|
|
|
1958
1995
|
setForking(false);
|
|
1959
1996
|
const threadId = targetApp.threadId;
|
|
1960
1997
|
if (!threadId) throw new Error("No thread available for this app.");
|
|
1998
|
+
onEditStart == null ? void 0 : onEditStart();
|
|
1961
1999
|
let attachmentMetas;
|
|
1962
2000
|
if (attachments && attachments.length > 0 && uploadAttachments) {
|
|
1963
2001
|
attachmentMetas = await uploadAttachments({ threadId, appId: targetApp.id, dataUrls: attachments });
|
|
1964
2002
|
}
|
|
1965
|
-
await agentRepository.editApp({
|
|
2003
|
+
const editResult = await agentRepository.editApp({
|
|
1966
2004
|
prompt,
|
|
1967
2005
|
thread_id: threadId,
|
|
1968
2006
|
app_id: targetApp.id,
|
|
1969
2007
|
attachments: attachmentMetas && attachmentMetas.length > 0 ? attachmentMetas : void 0
|
|
1970
2008
|
});
|
|
2009
|
+
onEditQueued == null ? void 0 : onEditQueued({
|
|
2010
|
+
queueItemId: editResult.queueItemId ?? null,
|
|
2011
|
+
queuePosition: editResult.queuePosition ?? null
|
|
2012
|
+
});
|
|
1971
2013
|
} catch (e) {
|
|
1972
2014
|
const err = e instanceof Error ? e : new Error(String(e));
|
|
1973
2015
|
setError(err);
|
|
@@ -1975,32 +2017,14 @@ function useStudioActions({
|
|
|
1975
2017
|
} finally {
|
|
1976
2018
|
setForking(false);
|
|
1977
2019
|
setSending(false);
|
|
2020
|
+
onEditFinished == null ? void 0 : onEditFinished();
|
|
1978
2021
|
}
|
|
1979
2022
|
},
|
|
1980
|
-
[app, onForkedApp, sending, shouldForkOnEdit, uploadAttachments, userId]
|
|
2023
|
+
[app, onEditFinished, onEditQueued, onEditStart, onForkedApp, sending, shouldForkOnEdit, uploadAttachments, userId]
|
|
1981
2024
|
);
|
|
1982
2025
|
return { isOwner, shouldForkOnEdit, forking, sending, error, sendEdit };
|
|
1983
2026
|
}
|
|
1984
2027
|
|
|
1985
|
-
// src/studio/lib/chat.ts
|
|
1986
|
-
function hasNoOutcomeAfterLastHuman(messages) {
|
|
1987
|
-
if (!messages || messages.length === 0) return false;
|
|
1988
|
-
let lastHumanIndex = -1;
|
|
1989
|
-
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
1990
|
-
if (messages[i].authorType === "human") {
|
|
1991
|
-
lastHumanIndex = i;
|
|
1992
|
-
break;
|
|
1993
|
-
}
|
|
1994
|
-
}
|
|
1995
|
-
if (lastHumanIndex === -1) return false;
|
|
1996
|
-
for (let i = lastHumanIndex + 1; i < messages.length; i += 1) {
|
|
1997
|
-
const m = messages[i];
|
|
1998
|
-
const payload = m.payload;
|
|
1999
|
-
if (m.authorType === "ai" && (payload == null ? void 0 : payload.type) === "outcome") return false;
|
|
2000
|
-
}
|
|
2001
|
-
return true;
|
|
2002
|
-
}
|
|
2003
|
-
|
|
2004
2028
|
// src/studio/ui/RuntimeRenderer.tsx
|
|
2005
2029
|
var React9 = __toESM(require("react"));
|
|
2006
2030
|
var import_react_native6 = require("react-native");
|
|
@@ -2038,8 +2062,8 @@ function RuntimeRenderer({
|
|
|
2038
2062
|
}
|
|
2039
2063
|
|
|
2040
2064
|
// src/studio/ui/StudioOverlay.tsx
|
|
2041
|
-
var
|
|
2042
|
-
var
|
|
2065
|
+
var React43 = __toESM(require("react"));
|
|
2066
|
+
var import_react_native55 = require("react-native");
|
|
2043
2067
|
|
|
2044
2068
|
// src/components/studio-sheet/StudioBottomSheet.tsx
|
|
2045
2069
|
var React12 = __toESM(require("react"));
|
|
@@ -5843,8 +5867,8 @@ function PreviewPanel({
|
|
|
5843
5867
|
}
|
|
5844
5868
|
|
|
5845
5869
|
// src/studio/ui/ChatPanel.tsx
|
|
5846
|
-
var
|
|
5847
|
-
var
|
|
5870
|
+
var React40 = __toESM(require("react"));
|
|
5871
|
+
var import_react_native52 = require("react-native");
|
|
5848
5872
|
|
|
5849
5873
|
// src/components/chat/ChatPage.tsx
|
|
5850
5874
|
var React37 = __toESM(require("react"));
|
|
@@ -6041,6 +6065,7 @@ function ChatPage({
|
|
|
6041
6065
|
showTypingIndicator,
|
|
6042
6066
|
renderMessageContent,
|
|
6043
6067
|
topBanner,
|
|
6068
|
+
composerTop,
|
|
6044
6069
|
composer,
|
|
6045
6070
|
overlay,
|
|
6046
6071
|
style,
|
|
@@ -6051,6 +6076,7 @@ function ChatPage({
|
|
|
6051
6076
|
const theme = useTheme();
|
|
6052
6077
|
const insets = (0, import_react_native_safe_area_context4.useSafeAreaInsets)();
|
|
6053
6078
|
const [composerHeight, setComposerHeight] = React37.useState(0);
|
|
6079
|
+
const [composerTopHeight, setComposerTopHeight] = React37.useState(0);
|
|
6054
6080
|
const [keyboardVisible, setKeyboardVisible] = React37.useState(false);
|
|
6055
6081
|
React37.useEffect(() => {
|
|
6056
6082
|
if (import_react_native47.Platform.OS !== "ios") return;
|
|
@@ -6062,8 +6088,9 @@ function ChatPage({
|
|
|
6062
6088
|
};
|
|
6063
6089
|
}, []);
|
|
6064
6090
|
const footerBottomPadding = import_react_native47.Platform.OS === "ios" ? keyboardVisible ? 0 : insets.bottom : insets.bottom + 10;
|
|
6065
|
-
const
|
|
6066
|
-
const
|
|
6091
|
+
const totalComposerHeight = composerHeight + composerTopHeight;
|
|
6092
|
+
const overlayBottom = totalComposerHeight + footerBottomPadding + theme.spacing.lg;
|
|
6093
|
+
const bottomInset = totalComposerHeight + footerBottomPadding + theme.spacing.xl;
|
|
6067
6094
|
const resolvedOverlay = React37.useMemo(() => {
|
|
6068
6095
|
var _a;
|
|
6069
6096
|
if (!overlay) return null;
|
|
@@ -6073,6 +6100,10 @@ function ChatPage({
|
|
|
6073
6100
|
style: [prevStyle, { bottom: overlayBottom }]
|
|
6074
6101
|
});
|
|
6075
6102
|
}, [overlay, overlayBottom]);
|
|
6103
|
+
React37.useEffect(() => {
|
|
6104
|
+
if (composerTop) return;
|
|
6105
|
+
setComposerTopHeight(0);
|
|
6106
|
+
}, [composerTop]);
|
|
6076
6107
|
return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_react_native47.View, { style: [{ flex: 1 }, style], children: [
|
|
6077
6108
|
header ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_react_native47.View, { children: header }) : null,
|
|
6078
6109
|
topBanner ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_react_native47.View, { style: { paddingHorizontal: theme.spacing.lg, paddingTop: theme.spacing.sm }, children: topBanner }) : null,
|
|
@@ -6097,7 +6128,7 @@ function ChatPage({
|
|
|
6097
6128
|
]
|
|
6098
6129
|
}
|
|
6099
6130
|
),
|
|
6100
|
-
/* @__PURE__ */ (0, import_jsx_runtime49.
|
|
6131
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
6101
6132
|
import_react_native47.View,
|
|
6102
6133
|
{
|
|
6103
6134
|
style: {
|
|
@@ -6109,14 +6140,24 @@ function ChatPage({
|
|
|
6109
6140
|
paddingTop: theme.spacing.sm,
|
|
6110
6141
|
paddingBottom: footerBottomPadding
|
|
6111
6142
|
},
|
|
6112
|
-
children:
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
|
|
6118
|
-
|
|
6119
|
-
|
|
6143
|
+
children: [
|
|
6144
|
+
composerTop ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
6145
|
+
import_react_native47.View,
|
|
6146
|
+
{
|
|
6147
|
+
style: { marginBottom: theme.spacing.sm },
|
|
6148
|
+
onLayout: (e) => setComposerTopHeight(e.nativeEvent.layout.height),
|
|
6149
|
+
children: composerTop
|
|
6150
|
+
}
|
|
6151
|
+
) : null,
|
|
6152
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
6153
|
+
ChatComposer,
|
|
6154
|
+
{
|
|
6155
|
+
...composer,
|
|
6156
|
+
attachments: composer.attachments ?? [],
|
|
6157
|
+
onLayout: ({ height }) => setComposerHeight(height)
|
|
6158
|
+
}
|
|
6159
|
+
)
|
|
6160
|
+
]
|
|
6120
6161
|
}
|
|
6121
6162
|
)
|
|
6122
6163
|
] })
|
|
@@ -6266,8 +6307,154 @@ function ForkNoticeBanner({ isOwner = true, title, description, style }) {
|
|
|
6266
6307
|
);
|
|
6267
6308
|
}
|
|
6268
6309
|
|
|
6269
|
-
// src/
|
|
6310
|
+
// src/components/chat/ChatQueue.tsx
|
|
6311
|
+
var React39 = __toESM(require("react"));
|
|
6312
|
+
var import_react_native51 = require("react-native");
|
|
6270
6313
|
var import_jsx_runtime53 = require("react/jsx-runtime");
|
|
6314
|
+
function ChatQueue({ items, onRemove }) {
|
|
6315
|
+
const theme = useTheme();
|
|
6316
|
+
const [expanded, setExpanded] = React39.useState({});
|
|
6317
|
+
const [canExpand, setCanExpand] = React39.useState({});
|
|
6318
|
+
const [collapsedText, setCollapsedText] = React39.useState({});
|
|
6319
|
+
const [removing, setRemoving] = React39.useState({});
|
|
6320
|
+
const buildCollapsedText = React39.useCallback((lines) => {
|
|
6321
|
+
var _a, _b;
|
|
6322
|
+
const line1 = ((_a = lines[0]) == null ? void 0 : _a.text) ?? "";
|
|
6323
|
+
const line2 = ((_b = lines[1]) == null ? void 0 : _b.text) ?? "";
|
|
6324
|
+
const moreLabel = "more";
|
|
6325
|
+
const reserve = `\u2026 ${moreLabel}`.length;
|
|
6326
|
+
let trimmedLine2 = line2;
|
|
6327
|
+
if (trimmedLine2.length > reserve) {
|
|
6328
|
+
trimmedLine2 = trimmedLine2.slice(0, Math.max(0, trimmedLine2.length - reserve));
|
|
6329
|
+
} else {
|
|
6330
|
+
trimmedLine2 = "";
|
|
6331
|
+
}
|
|
6332
|
+
trimmedLine2 = trimmedLine2.replace(/\s+$/, "");
|
|
6333
|
+
return `${line1}
|
|
6334
|
+
${trimmedLine2}\u2026 `;
|
|
6335
|
+
}, []);
|
|
6336
|
+
React39.useEffect(() => {
|
|
6337
|
+
if (items.length === 0) return;
|
|
6338
|
+
const ids = new Set(items.map((item) => item.id));
|
|
6339
|
+
setExpanded((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => ids.has(id))));
|
|
6340
|
+
setCanExpand((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => ids.has(id))));
|
|
6341
|
+
setCollapsedText((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => ids.has(id))));
|
|
6342
|
+
setRemoving((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => ids.has(id))));
|
|
6343
|
+
}, [items]);
|
|
6344
|
+
if (items.length === 0) return null;
|
|
6345
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
6346
|
+
import_react_native51.View,
|
|
6347
|
+
{
|
|
6348
|
+
style: {
|
|
6349
|
+
borderWidth: 1,
|
|
6350
|
+
borderColor: theme.colors.border,
|
|
6351
|
+
borderRadius: theme.radii.lg,
|
|
6352
|
+
marginHorizontal: theme.spacing.md,
|
|
6353
|
+
padding: theme.spacing.md,
|
|
6354
|
+
backgroundColor: "transparent"
|
|
6355
|
+
},
|
|
6356
|
+
children: [
|
|
6357
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Text, { variant: "caption", style: { marginBottom: theme.spacing.sm }, children: "Queue" }),
|
|
6358
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native51.View, { style: { gap: theme.spacing.sm }, children: items.map((item) => {
|
|
6359
|
+
const isExpanded = Boolean(expanded[item.id]);
|
|
6360
|
+
const showToggle = Boolean(canExpand[item.id]);
|
|
6361
|
+
const prompt = item.prompt ?? "";
|
|
6362
|
+
const moreLabel = "more";
|
|
6363
|
+
const displayPrompt = !isExpanded && showToggle && collapsedText[item.id] ? collapsedText[item.id] : prompt;
|
|
6364
|
+
const isRemoving = Boolean(removing[item.id]);
|
|
6365
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
6366
|
+
import_react_native51.View,
|
|
6367
|
+
{
|
|
6368
|
+
style: {
|
|
6369
|
+
flexDirection: "row",
|
|
6370
|
+
alignItems: "flex-start",
|
|
6371
|
+
gap: theme.spacing.sm,
|
|
6372
|
+
paddingHorizontal: theme.spacing.md,
|
|
6373
|
+
paddingVertical: theme.spacing.sm,
|
|
6374
|
+
borderRadius: theme.radii.md,
|
|
6375
|
+
backgroundColor: withAlpha(theme.colors.surface, theme.scheme === "dark" ? 0.8 : 0.9)
|
|
6376
|
+
},
|
|
6377
|
+
children: [
|
|
6378
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_react_native51.View, { style: { flex: 1 }, children: [
|
|
6379
|
+
!canExpand[item.id] ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
6380
|
+
Text,
|
|
6381
|
+
{
|
|
6382
|
+
style: { position: "absolute", opacity: 0, zIndex: -1, width: "100%" },
|
|
6383
|
+
onTextLayout: (e) => {
|
|
6384
|
+
var _a;
|
|
6385
|
+
const lines = (_a = e.nativeEvent) == null ? void 0 : _a.lines;
|
|
6386
|
+
if (!lines) return;
|
|
6387
|
+
if (lines.length > 2) {
|
|
6388
|
+
setCanExpand((prev) => ({ ...prev, [item.id]: true }));
|
|
6389
|
+
setCollapsedText((prev) => ({
|
|
6390
|
+
...prev,
|
|
6391
|
+
[item.id]: buildCollapsedText(lines)
|
|
6392
|
+
}));
|
|
6393
|
+
}
|
|
6394
|
+
},
|
|
6395
|
+
children: prompt
|
|
6396
|
+
}
|
|
6397
|
+
) : null,
|
|
6398
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
6399
|
+
Text,
|
|
6400
|
+
{
|
|
6401
|
+
variant: "bodyMuted",
|
|
6402
|
+
numberOfLines: isExpanded ? void 0 : 2,
|
|
6403
|
+
children: [
|
|
6404
|
+
displayPrompt,
|
|
6405
|
+
!isExpanded && showToggle ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
6406
|
+
Text,
|
|
6407
|
+
{
|
|
6408
|
+
color: theme.colors.text,
|
|
6409
|
+
onPress: () => setExpanded((prev) => ({ ...prev, [item.id]: true })),
|
|
6410
|
+
suppressHighlighting: true,
|
|
6411
|
+
children: moreLabel
|
|
6412
|
+
}
|
|
6413
|
+
) : null
|
|
6414
|
+
]
|
|
6415
|
+
}
|
|
6416
|
+
),
|
|
6417
|
+
showToggle && isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
6418
|
+
import_react_native51.Pressable,
|
|
6419
|
+
{
|
|
6420
|
+
onPress: () => setExpanded((prev) => ({ ...prev, [item.id]: false })),
|
|
6421
|
+
hitSlop: 6,
|
|
6422
|
+
style: { alignSelf: "flex-start", marginTop: 4 },
|
|
6423
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Text, { variant: "captionMuted", color: theme.colors.text, children: "less" })
|
|
6424
|
+
}
|
|
6425
|
+
) : null
|
|
6426
|
+
] }),
|
|
6427
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
6428
|
+
import_react_native51.Pressable,
|
|
6429
|
+
{
|
|
6430
|
+
onPress: () => {
|
|
6431
|
+
if (!onRemove || isRemoving) return;
|
|
6432
|
+
setRemoving((prev) => ({ ...prev, [item.id]: true }));
|
|
6433
|
+
Promise.resolve(onRemove(item.id)).finally(() => {
|
|
6434
|
+
setRemoving((prev) => {
|
|
6435
|
+
if (!prev[item.id]) return prev;
|
|
6436
|
+
const { [item.id]: _removed, ...rest } = prev;
|
|
6437
|
+
return rest;
|
|
6438
|
+
});
|
|
6439
|
+
});
|
|
6440
|
+
},
|
|
6441
|
+
hitSlop: 8,
|
|
6442
|
+
style: { alignSelf: "center" },
|
|
6443
|
+
children: isRemoving ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native51.ActivityIndicator, { size: "small", color: theme.colors.text }) : /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(IconClose, { size: 14, colorToken: "text" })
|
|
6444
|
+
}
|
|
6445
|
+
)
|
|
6446
|
+
]
|
|
6447
|
+
},
|
|
6448
|
+
item.id
|
|
6449
|
+
);
|
|
6450
|
+
}) })
|
|
6451
|
+
]
|
|
6452
|
+
}
|
|
6453
|
+
);
|
|
6454
|
+
}
|
|
6455
|
+
|
|
6456
|
+
// src/studio/ui/ChatPanel.tsx
|
|
6457
|
+
var import_jsx_runtime54 = require("react/jsx-runtime");
|
|
6271
6458
|
function ChatPanel({
|
|
6272
6459
|
title = "Chat",
|
|
6273
6460
|
autoFocusComposer = false,
|
|
@@ -6285,11 +6472,13 @@ function ChatPanel({
|
|
|
6285
6472
|
onClose,
|
|
6286
6473
|
onNavigateHome,
|
|
6287
6474
|
onStartDraw,
|
|
6288
|
-
onSend
|
|
6475
|
+
onSend,
|
|
6476
|
+
queueItems = [],
|
|
6477
|
+
onRemoveQueueItem
|
|
6289
6478
|
}) {
|
|
6290
|
-
const listRef =
|
|
6291
|
-
const [nearBottom, setNearBottom] =
|
|
6292
|
-
const handleSend =
|
|
6479
|
+
const listRef = React40.useRef(null);
|
|
6480
|
+
const [nearBottom, setNearBottom] = React40.useState(true);
|
|
6481
|
+
const handleSend = React40.useCallback(
|
|
6293
6482
|
async (text, composerAttachments) => {
|
|
6294
6483
|
const all = composerAttachments ?? attachments;
|
|
6295
6484
|
await onSend(text, all.length > 0 ? all : void 0);
|
|
@@ -6303,25 +6492,25 @@ function ChatPanel({
|
|
|
6303
6492
|
},
|
|
6304
6493
|
[attachments, nearBottom, onClearAttachments, onSend]
|
|
6305
6494
|
);
|
|
6306
|
-
const handleScrollToBottom =
|
|
6495
|
+
const handleScrollToBottom = React40.useCallback(() => {
|
|
6307
6496
|
var _a;
|
|
6308
6497
|
(_a = listRef.current) == null ? void 0 : _a.scrollToBottom({ animated: true });
|
|
6309
6498
|
}, []);
|
|
6310
|
-
const header = /* @__PURE__ */ (0,
|
|
6499
|
+
const header = /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
6311
6500
|
ChatHeader,
|
|
6312
6501
|
{
|
|
6313
|
-
left: /* @__PURE__ */ (0,
|
|
6314
|
-
/* @__PURE__ */ (0,
|
|
6315
|
-
onNavigateHome ? /* @__PURE__ */ (0,
|
|
6502
|
+
left: /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native52.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
|
|
6503
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(StudioSheetHeaderIconButton, { onPress: onBack, accessibilityLabel: "Back", style: { marginRight: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(IconBack, { size: 20, colorToken: "floatingContent" }) }),
|
|
6504
|
+
onNavigateHome ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(StudioSheetHeaderIconButton, { onPress: onNavigateHome, accessibilityLabel: "Home", children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(IconHome, { size: 20, colorToken: "floatingContent" }) }) : null
|
|
6316
6505
|
] }),
|
|
6317
|
-
right: /* @__PURE__ */ (0,
|
|
6318
|
-
onStartDraw ? /* @__PURE__ */ (0,
|
|
6319
|
-
/* @__PURE__ */ (0,
|
|
6506
|
+
right: /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native52.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
|
|
6507
|
+
onStartDraw ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(StudioSheetHeaderIconButton, { onPress: onStartDraw, accessibilityLabel: "Draw", intent: "danger", style: { marginRight: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(IconDraw, { size: 20, colorToken: "onDanger" }) }) : null,
|
|
6508
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(IconClose, { size: 20, colorToken: "floatingContent" }) })
|
|
6320
6509
|
] }),
|
|
6321
6510
|
center: null
|
|
6322
6511
|
}
|
|
6323
6512
|
);
|
|
6324
|
-
const topBanner = shouldForkOnEdit ? /* @__PURE__ */ (0,
|
|
6513
|
+
const topBanner = shouldForkOnEdit ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
6325
6514
|
ForkNoticeBanner,
|
|
6326
6515
|
{
|
|
6327
6516
|
isOwner: !shouldForkOnEdit,
|
|
@@ -6330,33 +6519,35 @@ function ChatPanel({
|
|
|
6330
6519
|
) : null;
|
|
6331
6520
|
const showMessagesLoading = Boolean(loading) && messages.length === 0 || forking;
|
|
6332
6521
|
if (showMessagesLoading) {
|
|
6333
|
-
return /* @__PURE__ */ (0,
|
|
6334
|
-
/* @__PURE__ */ (0,
|
|
6335
|
-
topBanner ? /* @__PURE__ */ (0,
|
|
6336
|
-
/* @__PURE__ */ (0,
|
|
6337
|
-
/* @__PURE__ */ (0,
|
|
6338
|
-
/* @__PURE__ */ (0,
|
|
6339
|
-
/* @__PURE__ */ (0,
|
|
6522
|
+
return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native52.View, { style: { flex: 1 }, children: [
|
|
6523
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native52.View, { children: header }),
|
|
6524
|
+
topBanner ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native52.View, { style: { paddingHorizontal: 16, paddingTop: 8 }, children: topBanner }) : null,
|
|
6525
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native52.View, { style: { flex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 24, paddingVertical: 12 }, children: [
|
|
6526
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native52.ActivityIndicator, {}),
|
|
6527
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(import_react_native52.View, { style: { height: 12 } }),
|
|
6528
|
+
/* @__PURE__ */ (0, import_jsx_runtime54.jsx)(Text, { variant: "bodyMuted", children: forking ? "Creating your copy\u2026" : "Loading messages\u2026" })
|
|
6340
6529
|
] })
|
|
6341
6530
|
] });
|
|
6342
6531
|
}
|
|
6343
|
-
|
|
6532
|
+
const queueTop = queueItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(ChatQueue, { items: queueItems, onRemove: onRemoveQueueItem }) : null;
|
|
6533
|
+
return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
6344
6534
|
ChatPage,
|
|
6345
6535
|
{
|
|
6346
6536
|
header,
|
|
6347
6537
|
messages,
|
|
6348
6538
|
showTypingIndicator,
|
|
6349
6539
|
topBanner,
|
|
6540
|
+
composerTop: queueTop,
|
|
6350
6541
|
composerHorizontalPadding: 0,
|
|
6351
6542
|
listRef,
|
|
6352
6543
|
onNearBottomChange: setNearBottom,
|
|
6353
|
-
overlay: /* @__PURE__ */ (0,
|
|
6544
|
+
overlay: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
6354
6545
|
ScrollToBottomButton,
|
|
6355
6546
|
{
|
|
6356
6547
|
visible: !nearBottom,
|
|
6357
6548
|
onPress: handleScrollToBottom,
|
|
6358
6549
|
style: { bottom: 80 },
|
|
6359
|
-
children: /* @__PURE__ */ (0,
|
|
6550
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(IconArrowDown, { size: 20, colorToken: "floatingContent" })
|
|
6360
6551
|
}
|
|
6361
6552
|
),
|
|
6362
6553
|
composer: {
|
|
@@ -6377,12 +6568,12 @@ function ChatPanel({
|
|
|
6377
6568
|
}
|
|
6378
6569
|
|
|
6379
6570
|
// src/components/dialogs/ConfirmMergeRequestDialog.tsx
|
|
6380
|
-
var
|
|
6381
|
-
var
|
|
6571
|
+
var React41 = __toESM(require("react"));
|
|
6572
|
+
var import_react_native54 = require("react-native");
|
|
6382
6573
|
|
|
6383
6574
|
// src/components/primitives/Modal.tsx
|
|
6384
|
-
var
|
|
6385
|
-
var
|
|
6575
|
+
var import_react_native53 = require("react-native");
|
|
6576
|
+
var import_jsx_runtime55 = require("react/jsx-runtime");
|
|
6386
6577
|
function Modal({
|
|
6387
6578
|
visible,
|
|
6388
6579
|
onRequestClose,
|
|
@@ -6391,30 +6582,30 @@ function Modal({
|
|
|
6391
6582
|
contentStyle
|
|
6392
6583
|
}) {
|
|
6393
6584
|
const theme = useTheme();
|
|
6394
|
-
return /* @__PURE__ */ (0,
|
|
6395
|
-
|
|
6585
|
+
return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
6586
|
+
import_react_native53.Modal,
|
|
6396
6587
|
{
|
|
6397
6588
|
visible,
|
|
6398
6589
|
transparent: true,
|
|
6399
6590
|
animationType: "fade",
|
|
6400
6591
|
onRequestClose,
|
|
6401
|
-
children: /* @__PURE__ */ (0,
|
|
6402
|
-
/* @__PURE__ */ (0,
|
|
6403
|
-
|
|
6592
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_react_native53.View, { style: { flex: 1, backgroundColor: theme.colors.backdrop, justifyContent: "center", padding: theme.spacing.lg }, children: [
|
|
6593
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
6594
|
+
import_react_native53.Pressable,
|
|
6404
6595
|
{
|
|
6405
6596
|
accessibilityRole: "button",
|
|
6406
6597
|
onPress: dismissOnBackdropPress ? onRequestClose : void 0,
|
|
6407
6598
|
style: { position: "absolute", inset: 0 }
|
|
6408
6599
|
}
|
|
6409
6600
|
),
|
|
6410
|
-
/* @__PURE__ */ (0,
|
|
6601
|
+
/* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Card, { variant: "surfaceRaised", padded: true, style: [{ borderRadius: theme.radii.xl }, contentStyle], children })
|
|
6411
6602
|
] })
|
|
6412
6603
|
}
|
|
6413
6604
|
);
|
|
6414
6605
|
}
|
|
6415
6606
|
|
|
6416
6607
|
// src/components/dialogs/ConfirmMergeRequestDialog.tsx
|
|
6417
|
-
var
|
|
6608
|
+
var import_jsx_runtime56 = require("react/jsx-runtime");
|
|
6418
6609
|
function ConfirmMergeRequestDialog({
|
|
6419
6610
|
visible,
|
|
6420
6611
|
onOpenChange,
|
|
@@ -6425,14 +6616,14 @@ function ConfirmMergeRequestDialog({
|
|
|
6425
6616
|
onTestFirst
|
|
6426
6617
|
}) {
|
|
6427
6618
|
const theme = useTheme();
|
|
6428
|
-
const close =
|
|
6619
|
+
const close = React41.useCallback(() => onOpenChange(false), [onOpenChange]);
|
|
6429
6620
|
const canConfirm = Boolean(mergeRequest) && !approveDisabled;
|
|
6430
|
-
const handleConfirm =
|
|
6621
|
+
const handleConfirm = React41.useCallback(() => {
|
|
6431
6622
|
if (!mergeRequest) return;
|
|
6432
6623
|
onOpenChange(false);
|
|
6433
6624
|
void onConfirm();
|
|
6434
6625
|
}, [mergeRequest, onConfirm, onOpenChange]);
|
|
6435
|
-
const handleTestFirst =
|
|
6626
|
+
const handleTestFirst = React41.useCallback(() => {
|
|
6436
6627
|
if (!mergeRequest) return;
|
|
6437
6628
|
onOpenChange(false);
|
|
6438
6629
|
void onTestFirst(mergeRequest);
|
|
@@ -6444,7 +6635,7 @@ function ConfirmMergeRequestDialog({
|
|
|
6444
6635
|
justifyContent: "center",
|
|
6445
6636
|
alignSelf: "stretch"
|
|
6446
6637
|
};
|
|
6447
|
-
return /* @__PURE__ */ (0,
|
|
6638
|
+
return /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(
|
|
6448
6639
|
Modal,
|
|
6449
6640
|
{
|
|
6450
6641
|
visible,
|
|
@@ -6455,7 +6646,7 @@ function ConfirmMergeRequestDialog({
|
|
|
6455
6646
|
backgroundColor: theme.colors.background
|
|
6456
6647
|
},
|
|
6457
6648
|
children: [
|
|
6458
|
-
/* @__PURE__ */ (0,
|
|
6649
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native54.View, { children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
6459
6650
|
Text,
|
|
6460
6651
|
{
|
|
6461
6652
|
style: {
|
|
@@ -6467,9 +6658,9 @@ function ConfirmMergeRequestDialog({
|
|
|
6467
6658
|
children: "Are you sure you want to approve this merge request?"
|
|
6468
6659
|
}
|
|
6469
6660
|
) }),
|
|
6470
|
-
/* @__PURE__ */ (0,
|
|
6471
|
-
/* @__PURE__ */ (0,
|
|
6472
|
-
|
|
6661
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native54.View, { style: { marginTop: 16 }, children: [
|
|
6662
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
6663
|
+
import_react_native54.View,
|
|
6473
6664
|
{
|
|
6474
6665
|
style: [
|
|
6475
6666
|
fullWidthButtonBase,
|
|
@@ -6478,22 +6669,22 @@ function ConfirmMergeRequestDialog({
|
|
|
6478
6669
|
opacity: canConfirm ? 1 : 0.5
|
|
6479
6670
|
}
|
|
6480
6671
|
],
|
|
6481
|
-
children: /* @__PURE__ */ (0,
|
|
6482
|
-
|
|
6672
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
6673
|
+
import_react_native54.Pressable,
|
|
6483
6674
|
{
|
|
6484
6675
|
accessibilityRole: "button",
|
|
6485
6676
|
accessibilityLabel: "Approve Merge",
|
|
6486
6677
|
disabled: !canConfirm,
|
|
6487
6678
|
onPress: handleConfirm,
|
|
6488
6679
|
style: [fullWidthButtonBase, { flex: 1 }],
|
|
6489
|
-
children: /* @__PURE__ */ (0,
|
|
6680
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(Text, { style: { textAlign: "center", color: theme.colors.onPrimary }, children: "Approve Merge" })
|
|
6490
6681
|
}
|
|
6491
6682
|
)
|
|
6492
6683
|
}
|
|
6493
6684
|
),
|
|
6494
|
-
/* @__PURE__ */ (0,
|
|
6495
|
-
/* @__PURE__ */ (0,
|
|
6496
|
-
|
|
6685
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native54.View, { style: { height: 8 } }),
|
|
6686
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
6687
|
+
import_react_native54.View,
|
|
6497
6688
|
{
|
|
6498
6689
|
style: [
|
|
6499
6690
|
fullWidthButtonBase,
|
|
@@ -6504,22 +6695,22 @@ function ConfirmMergeRequestDialog({
|
|
|
6504
6695
|
opacity: isBuilding || !mergeRequest ? 0.5 : 1
|
|
6505
6696
|
}
|
|
6506
6697
|
],
|
|
6507
|
-
children: /* @__PURE__ */ (0,
|
|
6508
|
-
|
|
6698
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
6699
|
+
import_react_native54.Pressable,
|
|
6509
6700
|
{
|
|
6510
6701
|
accessibilityRole: "button",
|
|
6511
6702
|
accessibilityLabel: isBuilding ? "Preparing\u2026" : "Test edits first",
|
|
6512
6703
|
disabled: isBuilding || !mergeRequest,
|
|
6513
6704
|
onPress: handleTestFirst,
|
|
6514
6705
|
style: [fullWidthButtonBase, { flex: 1 }],
|
|
6515
|
-
children: /* @__PURE__ */ (0,
|
|
6706
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(Text, { style: { textAlign: "center", color: theme.colors.text }, children: isBuilding ? "Preparing\u2026" : "Test edits first" })
|
|
6516
6707
|
}
|
|
6517
6708
|
)
|
|
6518
6709
|
}
|
|
6519
6710
|
),
|
|
6520
|
-
/* @__PURE__ */ (0,
|
|
6521
|
-
/* @__PURE__ */ (0,
|
|
6522
|
-
|
|
6711
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native54.View, { style: { height: 8 } }),
|
|
6712
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
6713
|
+
import_react_native54.View,
|
|
6523
6714
|
{
|
|
6524
6715
|
style: [
|
|
6525
6716
|
fullWidthButtonBase,
|
|
@@ -6529,14 +6720,14 @@ function ConfirmMergeRequestDialog({
|
|
|
6529
6720
|
borderColor: theme.colors.border
|
|
6530
6721
|
}
|
|
6531
6722
|
],
|
|
6532
|
-
children: /* @__PURE__ */ (0,
|
|
6533
|
-
|
|
6723
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
6724
|
+
import_react_native54.Pressable,
|
|
6534
6725
|
{
|
|
6535
6726
|
accessibilityRole: "button",
|
|
6536
6727
|
accessibilityLabel: "Cancel",
|
|
6537
6728
|
onPress: close,
|
|
6538
6729
|
style: [fullWidthButtonBase, { flex: 1 }],
|
|
6539
|
-
children: /* @__PURE__ */ (0,
|
|
6730
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(Text, { style: { textAlign: "center", color: theme.colors.text }, children: "Cancel" })
|
|
6540
6731
|
}
|
|
6541
6732
|
)
|
|
6542
6733
|
}
|
|
@@ -6548,7 +6739,7 @@ function ConfirmMergeRequestDialog({
|
|
|
6548
6739
|
}
|
|
6549
6740
|
|
|
6550
6741
|
// src/studio/ui/ConfirmMergeFlow.tsx
|
|
6551
|
-
var
|
|
6742
|
+
var import_jsx_runtime57 = require("react/jsx-runtime");
|
|
6552
6743
|
function ConfirmMergeFlow({
|
|
6553
6744
|
visible,
|
|
6554
6745
|
onOpenChange,
|
|
@@ -6559,7 +6750,7 @@ function ConfirmMergeFlow({
|
|
|
6559
6750
|
onConfirm,
|
|
6560
6751
|
onTestFirst
|
|
6561
6752
|
}) {
|
|
6562
|
-
return /* @__PURE__ */ (0,
|
|
6753
|
+
return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
6563
6754
|
ConfirmMergeRequestDialog,
|
|
6564
6755
|
{
|
|
6565
6756
|
visible,
|
|
@@ -6581,11 +6772,11 @@ function ConfirmMergeFlow({
|
|
|
6581
6772
|
}
|
|
6582
6773
|
|
|
6583
6774
|
// src/studio/hooks/useOptimisticChatMessages.ts
|
|
6584
|
-
var
|
|
6775
|
+
var React42 = __toESM(require("react"));
|
|
6585
6776
|
function makeOptimisticId() {
|
|
6586
6777
|
return `optimistic:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 10)}`;
|
|
6587
6778
|
}
|
|
6588
|
-
function
|
|
6779
|
+
function toEpochMs2(createdAt) {
|
|
6589
6780
|
if (createdAt == null) return 0;
|
|
6590
6781
|
if (typeof createdAt === "number") return createdAt;
|
|
6591
6782
|
if (createdAt instanceof Date) return createdAt.getTime();
|
|
@@ -6604,7 +6795,7 @@ function isOptimisticResolvedByServer(chatMessages, o) {
|
|
|
6604
6795
|
for (const m of candidates) {
|
|
6605
6796
|
if (m.author !== "human") continue;
|
|
6606
6797
|
if (normalize(m.content) !== target) continue;
|
|
6607
|
-
const serverMs =
|
|
6798
|
+
const serverMs = toEpochMs2(m.createdAt);
|
|
6608
6799
|
const optimisticMs = Date.parse(o.createdAtIso);
|
|
6609
6800
|
if (Number.isFinite(optimisticMs) && optimisticMs > 0 && serverMs > 0) {
|
|
6610
6801
|
if (serverMs + 12e4 < optimisticMs) continue;
|
|
@@ -6616,14 +6807,15 @@ function isOptimisticResolvedByServer(chatMessages, o) {
|
|
|
6616
6807
|
function useOptimisticChatMessages({
|
|
6617
6808
|
threadId,
|
|
6618
6809
|
shouldForkOnEdit,
|
|
6810
|
+
disableOptimistic = false,
|
|
6619
6811
|
chatMessages,
|
|
6620
6812
|
onSendChat
|
|
6621
6813
|
}) {
|
|
6622
|
-
const [optimisticChat, setOptimisticChat] =
|
|
6623
|
-
|
|
6814
|
+
const [optimisticChat, setOptimisticChat] = React42.useState([]);
|
|
6815
|
+
React42.useEffect(() => {
|
|
6624
6816
|
setOptimisticChat([]);
|
|
6625
6817
|
}, [threadId]);
|
|
6626
|
-
const messages =
|
|
6818
|
+
const messages = React42.useMemo(() => {
|
|
6627
6819
|
if (!optimisticChat || optimisticChat.length === 0) return chatMessages;
|
|
6628
6820
|
const unresolved = optimisticChat.filter((o) => !isOptimisticResolvedByServer(chatMessages, o));
|
|
6629
6821
|
if (unresolved.length === 0) return chatMessages;
|
|
@@ -6639,7 +6831,7 @@ function useOptimisticChatMessages({
|
|
|
6639
6831
|
merged.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
|
|
6640
6832
|
return merged;
|
|
6641
6833
|
}, [chatMessages, optimisticChat]);
|
|
6642
|
-
|
|
6834
|
+
React42.useEffect(() => {
|
|
6643
6835
|
if (optimisticChat.length === 0) return;
|
|
6644
6836
|
setOptimisticChat((prev) => {
|
|
6645
6837
|
if (prev.length === 0) return prev;
|
|
@@ -6647,9 +6839,9 @@ function useOptimisticChatMessages({
|
|
|
6647
6839
|
return next.length === prev.length ? prev : next;
|
|
6648
6840
|
});
|
|
6649
6841
|
}, [chatMessages, optimisticChat.length]);
|
|
6650
|
-
const onSend =
|
|
6842
|
+
const onSend = React42.useCallback(
|
|
6651
6843
|
async (text, attachments) => {
|
|
6652
|
-
if (shouldForkOnEdit) {
|
|
6844
|
+
if (shouldForkOnEdit || disableOptimistic) {
|
|
6653
6845
|
await onSendChat(text, attachments);
|
|
6654
6846
|
return;
|
|
6655
6847
|
}
|
|
@@ -6661,14 +6853,14 @@ function useOptimisticChatMessages({
|
|
|
6661
6853
|
setOptimisticChat((prev) => prev.map((m) => m.id === id ? { ...m, failed: true } : m));
|
|
6662
6854
|
});
|
|
6663
6855
|
},
|
|
6664
|
-
[chatMessages, onSendChat, shouldForkOnEdit]
|
|
6856
|
+
[chatMessages, disableOptimistic, onSendChat, shouldForkOnEdit]
|
|
6665
6857
|
);
|
|
6666
6858
|
return { messages, onSend };
|
|
6667
6859
|
}
|
|
6668
6860
|
|
|
6669
6861
|
// src/studio/ui/StudioOverlay.tsx
|
|
6670
6862
|
var import_studio_control = require("@comergehq/studio-control");
|
|
6671
|
-
var
|
|
6863
|
+
var import_jsx_runtime58 = require("react/jsx-runtime");
|
|
6672
6864
|
function StudioOverlay({
|
|
6673
6865
|
captureTargetRef,
|
|
6674
6866
|
app,
|
|
@@ -6695,46 +6887,52 @@ function StudioOverlay({
|
|
|
6695
6887
|
chatSending,
|
|
6696
6888
|
chatShowTypingIndicator,
|
|
6697
6889
|
onSendChat,
|
|
6890
|
+
chatQueueItems,
|
|
6891
|
+
onRemoveQueueItem,
|
|
6698
6892
|
onNavigateHome,
|
|
6699
6893
|
showBubble,
|
|
6700
6894
|
studioControlOptions
|
|
6701
6895
|
}) {
|
|
6702
6896
|
const theme = useTheme();
|
|
6703
|
-
const { width } = (0,
|
|
6704
|
-
const [sheetOpen, setSheetOpen] =
|
|
6705
|
-
const sheetOpenRef =
|
|
6706
|
-
const [activePage, setActivePage] =
|
|
6707
|
-
const [drawing, setDrawing] =
|
|
6708
|
-
const [chatAttachments, setChatAttachments] =
|
|
6709
|
-
const [commentsAppId, setCommentsAppId] =
|
|
6710
|
-
const [commentsCount, setCommentsCount] =
|
|
6897
|
+
const { width } = (0, import_react_native55.useWindowDimensions)();
|
|
6898
|
+
const [sheetOpen, setSheetOpen] = React43.useState(false);
|
|
6899
|
+
const sheetOpenRef = React43.useRef(sheetOpen);
|
|
6900
|
+
const [activePage, setActivePage] = React43.useState("preview");
|
|
6901
|
+
const [drawing, setDrawing] = React43.useState(false);
|
|
6902
|
+
const [chatAttachments, setChatAttachments] = React43.useState([]);
|
|
6903
|
+
const [commentsAppId, setCommentsAppId] = React43.useState(null);
|
|
6904
|
+
const [commentsCount, setCommentsCount] = React43.useState(null);
|
|
6711
6905
|
const threadId = (app == null ? void 0 : app.threadId) ?? null;
|
|
6906
|
+
const isForking = chatForking || (app == null ? void 0 : app.status) === "forking";
|
|
6907
|
+
const queueItemsForChat = isForking ? [] : chatQueueItems;
|
|
6908
|
+
const disableOptimistic = Boolean(queueItemsForChat && queueItemsForChat.length > 0) || (app == null ? void 0 : app.status) === "editing";
|
|
6712
6909
|
const optimistic = useOptimisticChatMessages({
|
|
6713
6910
|
threadId,
|
|
6714
6911
|
shouldForkOnEdit,
|
|
6912
|
+
disableOptimistic,
|
|
6715
6913
|
chatMessages,
|
|
6716
6914
|
onSendChat
|
|
6717
6915
|
});
|
|
6718
|
-
const [confirmMrId, setConfirmMrId] =
|
|
6719
|
-
const confirmMr =
|
|
6916
|
+
const [confirmMrId, setConfirmMrId] = React43.useState(null);
|
|
6917
|
+
const confirmMr = React43.useMemo(
|
|
6720
6918
|
() => confirmMrId ? incomingMergeRequests.find((m) => m.id === confirmMrId) ?? null : null,
|
|
6721
6919
|
[confirmMrId, incomingMergeRequests]
|
|
6722
6920
|
);
|
|
6723
|
-
const handleSheetOpenChange =
|
|
6921
|
+
const handleSheetOpenChange = React43.useCallback((open) => {
|
|
6724
6922
|
setSheetOpen(open);
|
|
6725
|
-
if (!open)
|
|
6923
|
+
if (!open) import_react_native55.Keyboard.dismiss();
|
|
6726
6924
|
}, []);
|
|
6727
|
-
const closeSheet =
|
|
6925
|
+
const closeSheet = React43.useCallback(() => {
|
|
6728
6926
|
handleSheetOpenChange(false);
|
|
6729
6927
|
}, [handleSheetOpenChange]);
|
|
6730
|
-
const openSheet =
|
|
6731
|
-
const goToChat =
|
|
6928
|
+
const openSheet = React43.useCallback(() => setSheetOpen(true), []);
|
|
6929
|
+
const goToChat = React43.useCallback(() => {
|
|
6732
6930
|
setActivePage("chat");
|
|
6733
6931
|
openSheet();
|
|
6734
6932
|
}, [openSheet]);
|
|
6735
|
-
const backToPreview =
|
|
6736
|
-
if (
|
|
6737
|
-
|
|
6933
|
+
const backToPreview = React43.useCallback(() => {
|
|
6934
|
+
if (import_react_native55.Platform.OS !== "ios") {
|
|
6935
|
+
import_react_native55.Keyboard.dismiss();
|
|
6738
6936
|
setActivePage("preview");
|
|
6739
6937
|
return;
|
|
6740
6938
|
}
|
|
@@ -6746,15 +6944,15 @@ function StudioOverlay({
|
|
|
6746
6944
|
clearTimeout(t);
|
|
6747
6945
|
setActivePage("preview");
|
|
6748
6946
|
};
|
|
6749
|
-
const sub =
|
|
6947
|
+
const sub = import_react_native55.Keyboard.addListener("keyboardDidHide", finalize);
|
|
6750
6948
|
const t = setTimeout(finalize, 350);
|
|
6751
|
-
|
|
6949
|
+
import_react_native55.Keyboard.dismiss();
|
|
6752
6950
|
}, []);
|
|
6753
|
-
const startDraw =
|
|
6951
|
+
const startDraw = React43.useCallback(() => {
|
|
6754
6952
|
setDrawing(true);
|
|
6755
6953
|
closeSheet();
|
|
6756
6954
|
}, [closeSheet]);
|
|
6757
|
-
const handleDrawCapture =
|
|
6955
|
+
const handleDrawCapture = React43.useCallback(
|
|
6758
6956
|
(dataUrl) => {
|
|
6759
6957
|
setChatAttachments((prev) => [...prev, dataUrl]);
|
|
6760
6958
|
setDrawing(false);
|
|
@@ -6763,7 +6961,7 @@ function StudioOverlay({
|
|
|
6763
6961
|
},
|
|
6764
6962
|
[openSheet]
|
|
6765
6963
|
);
|
|
6766
|
-
const toggleSheet =
|
|
6964
|
+
const toggleSheet = React43.useCallback(async () => {
|
|
6767
6965
|
if (!sheetOpen) {
|
|
6768
6966
|
const shouldExitTest = Boolean(testingMrId) || isTesting;
|
|
6769
6967
|
if (shouldExitTest) {
|
|
@@ -6775,7 +6973,7 @@ function StudioOverlay({
|
|
|
6775
6973
|
closeSheet();
|
|
6776
6974
|
}
|
|
6777
6975
|
}, [closeSheet, isTesting, onRestoreBase, sheetOpen, testingMrId]);
|
|
6778
|
-
const handleTestMr =
|
|
6976
|
+
const handleTestMr = React43.useCallback(
|
|
6779
6977
|
async (mr) => {
|
|
6780
6978
|
if (!onTestMr) return;
|
|
6781
6979
|
await onTestMr(mr);
|
|
@@ -6783,10 +6981,10 @@ function StudioOverlay({
|
|
|
6783
6981
|
},
|
|
6784
6982
|
[closeSheet, onTestMr]
|
|
6785
6983
|
);
|
|
6786
|
-
|
|
6984
|
+
React43.useEffect(() => {
|
|
6787
6985
|
sheetOpenRef.current = sheetOpen;
|
|
6788
6986
|
}, [sheetOpen]);
|
|
6789
|
-
|
|
6987
|
+
React43.useEffect(() => {
|
|
6790
6988
|
const poller = (0, import_studio_control.startStudioControlPolling)((action) => {
|
|
6791
6989
|
if (action === "show" && !sheetOpenRef.current) openSheet();
|
|
6792
6990
|
if (action === "hide" && sheetOpenRef.current) closeSheet();
|
|
@@ -6794,17 +6992,17 @@ function StudioOverlay({
|
|
|
6794
6992
|
}, studioControlOptions);
|
|
6795
6993
|
return () => poller.stop();
|
|
6796
6994
|
}, [closeSheet, openSheet, studioControlOptions, toggleSheet]);
|
|
6797
|
-
|
|
6995
|
+
React43.useEffect(() => {
|
|
6798
6996
|
void (0, import_studio_control.publishComergeStudioUIState)(sheetOpen, studioControlOptions);
|
|
6799
6997
|
}, [sheetOpen, studioControlOptions]);
|
|
6800
|
-
return /* @__PURE__ */ (0,
|
|
6801
|
-
/* @__PURE__ */ (0,
|
|
6802
|
-
/* @__PURE__ */ (0,
|
|
6998
|
+
return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_jsx_runtime58.Fragment, { children: [
|
|
6999
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(EdgeGlowFrame, { visible: isTesting, role: "accent", thickness: 40, intensity: 1 }),
|
|
7000
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(StudioBottomSheet, { open: sheetOpen, onOpenChange: handleSheetOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
6803
7001
|
StudioSheetPager,
|
|
6804
7002
|
{
|
|
6805
7003
|
activePage,
|
|
6806
7004
|
width,
|
|
6807
|
-
preview: /* @__PURE__ */ (0,
|
|
7005
|
+
preview: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
6808
7006
|
PreviewPanel,
|
|
6809
7007
|
{
|
|
6810
7008
|
app,
|
|
@@ -6830,7 +7028,7 @@ function StudioOverlay({
|
|
|
6830
7028
|
commentCountOverride: commentsCount ?? void 0
|
|
6831
7029
|
}
|
|
6832
7030
|
),
|
|
6833
|
-
chat: /* @__PURE__ */ (0,
|
|
7031
|
+
chat: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
6834
7032
|
ChatPanel,
|
|
6835
7033
|
{
|
|
6836
7034
|
messages: optimistic.messages,
|
|
@@ -6848,12 +7046,14 @@ function StudioOverlay({
|
|
|
6848
7046
|
onClose: closeSheet,
|
|
6849
7047
|
onNavigateHome,
|
|
6850
7048
|
onStartDraw: startDraw,
|
|
6851
|
-
onSend: optimistic.onSend
|
|
7049
|
+
onSend: optimistic.onSend,
|
|
7050
|
+
queueItems: queueItemsForChat,
|
|
7051
|
+
onRemoveQueueItem
|
|
6852
7052
|
}
|
|
6853
7053
|
)
|
|
6854
7054
|
}
|
|
6855
7055
|
) }),
|
|
6856
|
-
showBubble && /* @__PURE__ */ (0,
|
|
7056
|
+
showBubble && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
6857
7057
|
Bubble,
|
|
6858
7058
|
{
|
|
6859
7059
|
visible: !sheetOpen && !drawing,
|
|
@@ -6861,10 +7061,10 @@ function StudioOverlay({
|
|
|
6861
7061
|
badgeCount: incomingMergeRequests.length,
|
|
6862
7062
|
onPress: toggleSheet,
|
|
6863
7063
|
isLoading: (app == null ? void 0 : app.status) === "editing",
|
|
6864
|
-
children: /* @__PURE__ */ (0,
|
|
7064
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_react_native55.View, { style: { width: 28, height: 28, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(MergeIcon, { width: 24, height: 24, color: theme.colors.floatingContent }) })
|
|
6865
7065
|
}
|
|
6866
7066
|
),
|
|
6867
|
-
/* @__PURE__ */ (0,
|
|
7067
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
6868
7068
|
DrawModeOverlay,
|
|
6869
7069
|
{
|
|
6870
7070
|
visible: drawing,
|
|
@@ -6873,7 +7073,7 @@ function StudioOverlay({
|
|
|
6873
7073
|
onCapture: handleDrawCapture
|
|
6874
7074
|
}
|
|
6875
7075
|
),
|
|
6876
|
-
/* @__PURE__ */ (0,
|
|
7076
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
6877
7077
|
ConfirmMergeFlow,
|
|
6878
7078
|
{
|
|
6879
7079
|
visible: Boolean(confirmMr),
|
|
@@ -6886,7 +7086,7 @@ function StudioOverlay({
|
|
|
6886
7086
|
onTestFirst: handleTestMr
|
|
6887
7087
|
}
|
|
6888
7088
|
),
|
|
6889
|
-
/* @__PURE__ */ (0,
|
|
7089
|
+
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
6890
7090
|
AppCommentsSheet,
|
|
6891
7091
|
{
|
|
6892
7092
|
appId: commentsAppId,
|
|
@@ -6898,8 +7098,190 @@ function StudioOverlay({
|
|
|
6898
7098
|
] });
|
|
6899
7099
|
}
|
|
6900
7100
|
|
|
7101
|
+
// src/studio/hooks/useEditQueue.ts
|
|
7102
|
+
var React44 = __toESM(require("react"));
|
|
7103
|
+
|
|
7104
|
+
// src/data/apps/edit-queue/remote.ts
|
|
7105
|
+
var EditQueueRemoteDataSourceImpl = class extends BaseRemote {
|
|
7106
|
+
async list(appId) {
|
|
7107
|
+
const { data } = await api.get(
|
|
7108
|
+
`/v1/apps/${encodeURIComponent(appId)}/edit-queue`
|
|
7109
|
+
);
|
|
7110
|
+
return data;
|
|
7111
|
+
}
|
|
7112
|
+
async update(appId, queueItemId, payload) {
|
|
7113
|
+
const { data } = await api.patch(
|
|
7114
|
+
`/v1/apps/${encodeURIComponent(appId)}/edit-queue/${encodeURIComponent(queueItemId)}`,
|
|
7115
|
+
payload
|
|
7116
|
+
);
|
|
7117
|
+
return data;
|
|
7118
|
+
}
|
|
7119
|
+
async cancel(appId, queueItemId) {
|
|
7120
|
+
const { data } = await api.delete(
|
|
7121
|
+
`/v1/apps/${encodeURIComponent(appId)}/edit-queue/${encodeURIComponent(queueItemId)}`
|
|
7122
|
+
);
|
|
7123
|
+
return data;
|
|
7124
|
+
}
|
|
7125
|
+
};
|
|
7126
|
+
var editQueueRemoteDataSource = new EditQueueRemoteDataSourceImpl();
|
|
7127
|
+
|
|
7128
|
+
// src/data/apps/edit-queue/repository.ts
|
|
7129
|
+
var ACTIVE_STATUSES = ["pending"];
|
|
7130
|
+
function toString(value) {
|
|
7131
|
+
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
7132
|
+
}
|
|
7133
|
+
function toAttachments(value) {
|
|
7134
|
+
return Array.isArray(value) ? value : [];
|
|
7135
|
+
}
|
|
7136
|
+
function mapQueueItem(row) {
|
|
7137
|
+
const payload = row.payload ?? {};
|
|
7138
|
+
return {
|
|
7139
|
+
id: row.id,
|
|
7140
|
+
status: row.status,
|
|
7141
|
+
prompt: toString(payload.trimmedPrompt),
|
|
7142
|
+
messageId: toString(payload.messageId),
|
|
7143
|
+
attachments: toAttachments(payload.attachments),
|
|
7144
|
+
createdAt: row.created_at,
|
|
7145
|
+
updatedAt: row.updated_at,
|
|
7146
|
+
runAfter: row.run_after,
|
|
7147
|
+
priority: row.priority
|
|
7148
|
+
};
|
|
7149
|
+
}
|
|
7150
|
+
var EditQueueRepositoryImpl = class extends BaseRepository {
|
|
7151
|
+
constructor(remote) {
|
|
7152
|
+
super();
|
|
7153
|
+
this.remote = remote;
|
|
7154
|
+
}
|
|
7155
|
+
async list(appId) {
|
|
7156
|
+
const res = await this.remote.list(appId);
|
|
7157
|
+
const data = this.unwrapOrThrow(res);
|
|
7158
|
+
return data.items ?? [];
|
|
7159
|
+
}
|
|
7160
|
+
async update(appId, queueItemId, payload) {
|
|
7161
|
+
const res = await this.remote.update(appId, queueItemId, payload);
|
|
7162
|
+
return this.unwrapOrThrow(res);
|
|
7163
|
+
}
|
|
7164
|
+
async cancel(appId, queueItemId) {
|
|
7165
|
+
const res = await this.remote.cancel(appId, queueItemId);
|
|
7166
|
+
return this.unwrapOrThrow(res);
|
|
7167
|
+
}
|
|
7168
|
+
subscribeEditQueue(appId, handlers) {
|
|
7169
|
+
const supabase = getSupabaseClient();
|
|
7170
|
+
const channel = supabase.channel(`edit-queue:app:${appId}`).on(
|
|
7171
|
+
"postgres_changes",
|
|
7172
|
+
{ event: "INSERT", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7173
|
+
(payload) => {
|
|
7174
|
+
var _a;
|
|
7175
|
+
const row = payload.new;
|
|
7176
|
+
if (row.kind !== "edit") return;
|
|
7177
|
+
const item = mapQueueItem(row);
|
|
7178
|
+
if (!ACTIVE_STATUSES.includes(item.status)) return;
|
|
7179
|
+
(_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, item);
|
|
7180
|
+
}
|
|
7181
|
+
).on(
|
|
7182
|
+
"postgres_changes",
|
|
7183
|
+
{ event: "UPDATE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7184
|
+
(payload) => {
|
|
7185
|
+
var _a, _b;
|
|
7186
|
+
const row = payload.new;
|
|
7187
|
+
if (row.kind !== "edit") return;
|
|
7188
|
+
const item = mapQueueItem(row);
|
|
7189
|
+
if (ACTIVE_STATUSES.includes(item.status)) (_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, item);
|
|
7190
|
+
else (_b = handlers.onDelete) == null ? void 0 : _b.call(handlers, item);
|
|
7191
|
+
}
|
|
7192
|
+
).on(
|
|
7193
|
+
"postgres_changes",
|
|
7194
|
+
{ event: "DELETE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
|
|
7195
|
+
(payload) => {
|
|
7196
|
+
var _a;
|
|
7197
|
+
const row = payload.old;
|
|
7198
|
+
if (row.kind !== "edit") return;
|
|
7199
|
+
(_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapQueueItem(row));
|
|
7200
|
+
}
|
|
7201
|
+
).subscribe();
|
|
7202
|
+
return () => {
|
|
7203
|
+
supabase.removeChannel(channel);
|
|
7204
|
+
};
|
|
7205
|
+
}
|
|
7206
|
+
};
|
|
7207
|
+
var editQueueRepository = new EditQueueRepositoryImpl(
|
|
7208
|
+
editQueueRemoteDataSource
|
|
7209
|
+
);
|
|
7210
|
+
|
|
7211
|
+
// src/studio/hooks/useEditQueue.ts
|
|
7212
|
+
function useEditQueue(appId) {
|
|
7213
|
+
const [items, setItems] = React44.useState([]);
|
|
7214
|
+
const [loading, setLoading] = React44.useState(false);
|
|
7215
|
+
const [error, setError] = React44.useState(null);
|
|
7216
|
+
const activeRequestIdRef = React44.useRef(0);
|
|
7217
|
+
const foregroundSignal = useForegroundSignal(Boolean(appId));
|
|
7218
|
+
const upsertSorted = React44.useCallback((prev, nextItem) => {
|
|
7219
|
+
const next = prev.some((x) => x.id === nextItem.id) ? prev.map((x) => x.id === nextItem.id ? nextItem : x) : [...prev, nextItem];
|
|
7220
|
+
next.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
|
|
7221
|
+
return next;
|
|
7222
|
+
}, []);
|
|
7223
|
+
const refetch = React44.useCallback(async () => {
|
|
7224
|
+
if (!appId) {
|
|
7225
|
+
setItems([]);
|
|
7226
|
+
return;
|
|
7227
|
+
}
|
|
7228
|
+
const requestId = ++activeRequestIdRef.current;
|
|
7229
|
+
setLoading(true);
|
|
7230
|
+
setError(null);
|
|
7231
|
+
try {
|
|
7232
|
+
const list = await editQueueRepository.list(appId);
|
|
7233
|
+
if (activeRequestIdRef.current !== requestId) return;
|
|
7234
|
+
setItems([...list].sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt))));
|
|
7235
|
+
} catch (e) {
|
|
7236
|
+
if (activeRequestIdRef.current !== requestId) return;
|
|
7237
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
7238
|
+
setItems([]);
|
|
7239
|
+
} finally {
|
|
7240
|
+
if (activeRequestIdRef.current === requestId) setLoading(false);
|
|
7241
|
+
}
|
|
7242
|
+
}, [appId]);
|
|
7243
|
+
React44.useEffect(() => {
|
|
7244
|
+
void refetch();
|
|
7245
|
+
}, [refetch]);
|
|
7246
|
+
React44.useEffect(() => {
|
|
7247
|
+
if (!appId) return;
|
|
7248
|
+
const unsubscribe = editQueueRepository.subscribeEditQueue(appId, {
|
|
7249
|
+
onInsert: (item) => setItems((prev) => upsertSorted(prev, item)),
|
|
7250
|
+
onUpdate: (item) => setItems((prev) => upsertSorted(prev, item)),
|
|
7251
|
+
onDelete: (item) => setItems((prev) => prev.filter((x) => x.id !== item.id))
|
|
7252
|
+
});
|
|
7253
|
+
return unsubscribe;
|
|
7254
|
+
}, [appId, upsertSorted, foregroundSignal]);
|
|
7255
|
+
React44.useEffect(() => {
|
|
7256
|
+
if (!appId) return;
|
|
7257
|
+
if (foregroundSignal <= 0) return;
|
|
7258
|
+
void refetch();
|
|
7259
|
+
}, [appId, foregroundSignal, refetch]);
|
|
7260
|
+
return { items, loading, error, refetch };
|
|
7261
|
+
}
|
|
7262
|
+
|
|
7263
|
+
// src/studio/hooks/useEditQueueActions.ts
|
|
7264
|
+
var React45 = __toESM(require("react"));
|
|
7265
|
+
function useEditQueueActions(appId) {
|
|
7266
|
+
const update = React45.useCallback(
|
|
7267
|
+
async (queueItemId, payload) => {
|
|
7268
|
+
if (!appId) return;
|
|
7269
|
+
await editQueueRepository.update(appId, queueItemId, payload);
|
|
7270
|
+
},
|
|
7271
|
+
[appId]
|
|
7272
|
+
);
|
|
7273
|
+
const cancel = React45.useCallback(
|
|
7274
|
+
async (queueItemId) => {
|
|
7275
|
+
if (!appId) return;
|
|
7276
|
+
await editQueueRepository.cancel(appId, queueItemId);
|
|
7277
|
+
},
|
|
7278
|
+
[appId]
|
|
7279
|
+
);
|
|
7280
|
+
return { update, cancel };
|
|
7281
|
+
}
|
|
7282
|
+
|
|
6901
7283
|
// src/studio/ComergeStudio.tsx
|
|
6902
|
-
var
|
|
7284
|
+
var import_jsx_runtime59 = require("react/jsx-runtime");
|
|
6903
7285
|
function ComergeStudio({
|
|
6904
7286
|
appId,
|
|
6905
7287
|
clientKey: clientKey2,
|
|
@@ -6910,17 +7292,17 @@ function ComergeStudio({
|
|
|
6910
7292
|
studioControlOptions,
|
|
6911
7293
|
embeddedBaseBundles
|
|
6912
7294
|
}) {
|
|
6913
|
-
const [activeAppId, setActiveAppId] =
|
|
6914
|
-
const [runtimeAppId, setRuntimeAppId] =
|
|
6915
|
-
const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] =
|
|
6916
|
-
const platform =
|
|
6917
|
-
|
|
7295
|
+
const [activeAppId, setActiveAppId] = React46.useState(appId);
|
|
7296
|
+
const [runtimeAppId, setRuntimeAppId] = React46.useState(appId);
|
|
7297
|
+
const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React46.useState(null);
|
|
7298
|
+
const platform = React46.useMemo(() => import_react_native56.Platform.OS === "ios" ? "ios" : "android", []);
|
|
7299
|
+
React46.useEffect(() => {
|
|
6918
7300
|
setActiveAppId(appId);
|
|
6919
7301
|
setRuntimeAppId(appId);
|
|
6920
7302
|
setPendingRuntimeTargetAppId(null);
|
|
6921
7303
|
}, [appId]);
|
|
6922
|
-
const captureTargetRef =
|
|
6923
|
-
return /* @__PURE__ */ (0,
|
|
7304
|
+
const captureTargetRef = React46.useRef(null);
|
|
7305
|
+
return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(StudioBootstrap, { clientKey: clientKey2, fallback: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_react_native56.View, { style: { flex: 1 } }), children: ({ userId }) => /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_bottom_sheet6.BottomSheetModalProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(LiquidGlassResetProvider, { resetTriggers: [appId, activeAppId, runtimeAppId], children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
|
|
6924
7306
|
ComergeStudioInner,
|
|
6925
7307
|
{
|
|
6926
7308
|
userId,
|
|
@@ -6961,11 +7343,11 @@ function ComergeStudioInner({
|
|
|
6961
7343
|
const { app, loading: appLoading } = useApp(activeAppId);
|
|
6962
7344
|
const { app: runtimeAppFromHook } = useApp(runtimeAppId, { enabled: runtimeAppId !== activeAppId });
|
|
6963
7345
|
const runtimeApp = runtimeAppId === activeAppId ? app : runtimeAppFromHook;
|
|
6964
|
-
const sawEditingOnPendingTargetRef =
|
|
6965
|
-
|
|
7346
|
+
const sawEditingOnPendingTargetRef = React46.useRef(false);
|
|
7347
|
+
React46.useEffect(() => {
|
|
6966
7348
|
sawEditingOnPendingTargetRef.current = false;
|
|
6967
7349
|
}, [pendingRuntimeTargetAppId]);
|
|
6968
|
-
|
|
7350
|
+
React46.useEffect(() => {
|
|
6969
7351
|
if (!pendingRuntimeTargetAppId) return;
|
|
6970
7352
|
if (activeAppId !== pendingRuntimeTargetAppId) return;
|
|
6971
7353
|
if ((app == null ? void 0 : app.status) === "editing") {
|
|
@@ -6983,13 +7365,13 @@ function ComergeStudioInner({
|
|
|
6983
7365
|
canRequestLatest: (runtimeApp == null ? void 0 : runtimeApp.status) === "ready",
|
|
6984
7366
|
embeddedBaseBundles
|
|
6985
7367
|
});
|
|
6986
|
-
const sawEditingOnActiveAppRef =
|
|
6987
|
-
const [showPostEditPreparing, setShowPostEditPreparing] =
|
|
6988
|
-
|
|
7368
|
+
const sawEditingOnActiveAppRef = React46.useRef(false);
|
|
7369
|
+
const [showPostEditPreparing, setShowPostEditPreparing] = React46.useState(false);
|
|
7370
|
+
React46.useEffect(() => {
|
|
6989
7371
|
sawEditingOnActiveAppRef.current = false;
|
|
6990
7372
|
setShowPostEditPreparing(false);
|
|
6991
7373
|
}, [activeAppId]);
|
|
6992
|
-
|
|
7374
|
+
React46.useEffect(() => {
|
|
6993
7375
|
if (!(app == null ? void 0 : app.id)) return;
|
|
6994
7376
|
if (app.status === "editing") {
|
|
6995
7377
|
sawEditingOnActiveAppRef.current = true;
|
|
@@ -7001,7 +7383,7 @@ function ComergeStudioInner({
|
|
|
7001
7383
|
sawEditingOnActiveAppRef.current = false;
|
|
7002
7384
|
}
|
|
7003
7385
|
}, [app == null ? void 0 : app.id, app == null ? void 0 : app.status]);
|
|
7004
|
-
|
|
7386
|
+
React46.useEffect(() => {
|
|
7005
7387
|
if (!showPostEditPreparing) return;
|
|
7006
7388
|
const stillProcessingBaseBundle = bundle.loading && bundle.loadingMode === "base" && !bundle.isTesting;
|
|
7007
7389
|
if (!stillProcessingBaseBundle) {
|
|
@@ -7010,15 +7392,27 @@ function ComergeStudioInner({
|
|
|
7010
7392
|
}, [showPostEditPreparing, bundle.loading, bundle.loadingMode, bundle.isTesting]);
|
|
7011
7393
|
const threadId = (app == null ? void 0 : app.threadId) ?? "";
|
|
7012
7394
|
const thread = useThreadMessages(threadId);
|
|
7395
|
+
const editQueue = useEditQueue(activeAppId);
|
|
7396
|
+
const editQueueActions = useEditQueueActions(activeAppId);
|
|
7397
|
+
const [lastEditQueueInfo, setLastEditQueueInfo] = React46.useState(null);
|
|
7398
|
+
const lastEditQueueInfoRef = React46.useRef(null);
|
|
7399
|
+
const [suppressQueueUntilResponse, setSuppressQueueUntilResponse] = React46.useState(false);
|
|
7013
7400
|
const mergeRequests = useMergeRequests({ appId: activeAppId });
|
|
7014
|
-
const hasOpenOutgoingMr =
|
|
7401
|
+
const hasOpenOutgoingMr = React46.useMemo(() => {
|
|
7015
7402
|
return mergeRequests.lists.outgoing.some((mr) => mr.status === "open");
|
|
7016
7403
|
}, [mergeRequests.lists.outgoing]);
|
|
7017
|
-
const incomingReviewMrs =
|
|
7404
|
+
const incomingReviewMrs = React46.useMemo(() => {
|
|
7018
7405
|
if (!userId) return mergeRequests.lists.incoming;
|
|
7019
7406
|
return mergeRequests.lists.incoming.filter((mr) => mr.createdBy !== userId);
|
|
7020
7407
|
}, [mergeRequests.lists.incoming, userId]);
|
|
7021
7408
|
const uploader = useAttachmentUpload();
|
|
7409
|
+
const updateLastEditQueueInfo = React46.useCallback(
|
|
7410
|
+
(info) => {
|
|
7411
|
+
lastEditQueueInfoRef.current = info;
|
|
7412
|
+
setLastEditQueueInfo(info);
|
|
7413
|
+
},
|
|
7414
|
+
[]
|
|
7415
|
+
);
|
|
7022
7416
|
const actions = useStudioActions({
|
|
7023
7417
|
userId,
|
|
7024
7418
|
app,
|
|
@@ -7033,20 +7427,62 @@ function ComergeStudioInner({
|
|
|
7033
7427
|
setPendingRuntimeTargetAppId(null);
|
|
7034
7428
|
}
|
|
7035
7429
|
},
|
|
7036
|
-
uploadAttachments: uploader.uploadBase64Images
|
|
7430
|
+
uploadAttachments: uploader.uploadBase64Images,
|
|
7431
|
+
onEditStart: () => {
|
|
7432
|
+
if (editQueue.items.length === 0) {
|
|
7433
|
+
setSuppressQueueUntilResponse(true);
|
|
7434
|
+
}
|
|
7435
|
+
},
|
|
7436
|
+
onEditQueued: (info) => {
|
|
7437
|
+
updateLastEditQueueInfo(info);
|
|
7438
|
+
if (info.queuePosition !== 1) {
|
|
7439
|
+
setSuppressQueueUntilResponse(false);
|
|
7440
|
+
}
|
|
7441
|
+
},
|
|
7442
|
+
onEditFinished: () => {
|
|
7443
|
+
var _a;
|
|
7444
|
+
if (((_a = lastEditQueueInfoRef.current) == null ? void 0 : _a.queuePosition) !== 1) {
|
|
7445
|
+
setSuppressQueueUntilResponse(false);
|
|
7446
|
+
}
|
|
7447
|
+
}
|
|
7037
7448
|
});
|
|
7038
|
-
const chatSendDisabled =
|
|
7039
|
-
const [processingMrId, setProcessingMrId] =
|
|
7040
|
-
const [testingMrId, setTestingMrId] =
|
|
7041
|
-
const chatShowTypingIndicator =
|
|
7449
|
+
const chatSendDisabled = false;
|
|
7450
|
+
const [processingMrId, setProcessingMrId] = React46.useState(null);
|
|
7451
|
+
const [testingMrId, setTestingMrId] = React46.useState(null);
|
|
7452
|
+
const chatShowTypingIndicator = React46.useMemo(() => {
|
|
7042
7453
|
var _a;
|
|
7043
7454
|
if (!thread.raw || thread.raw.length === 0) return false;
|
|
7044
7455
|
const last = thread.raw[thread.raw.length - 1];
|
|
7045
7456
|
const payloadType = typeof ((_a = last.payload) == null ? void 0 : _a.type) === "string" ? String(last.payload.type) : void 0;
|
|
7046
7457
|
return payloadType !== "outcome";
|
|
7047
7458
|
}, [thread.raw]);
|
|
7048
|
-
|
|
7049
|
-
|
|
7459
|
+
React46.useEffect(() => {
|
|
7460
|
+
updateLastEditQueueInfo(null);
|
|
7461
|
+
setSuppressQueueUntilResponse(false);
|
|
7462
|
+
}, [activeAppId, updateLastEditQueueInfo]);
|
|
7463
|
+
React46.useEffect(() => {
|
|
7464
|
+
if (!(lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId)) return;
|
|
7465
|
+
const stillPresent = editQueue.items.some((item) => item.id === lastEditQueueInfo.queueItemId);
|
|
7466
|
+
if (!stillPresent) {
|
|
7467
|
+
updateLastEditQueueInfo(null);
|
|
7468
|
+
setSuppressQueueUntilResponse(false);
|
|
7469
|
+
}
|
|
7470
|
+
}, [editQueue.items, lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId]);
|
|
7471
|
+
const chatQueueItems = React46.useMemo(() => {
|
|
7472
|
+
var _a;
|
|
7473
|
+
if (suppressQueueUntilResponse && editQueue.items.length <= 1) {
|
|
7474
|
+
return [];
|
|
7475
|
+
}
|
|
7476
|
+
if (!lastEditQueueInfo || lastEditQueueInfo.queuePosition !== 1 || !lastEditQueueInfo.queueItemId) {
|
|
7477
|
+
return editQueue.items;
|
|
7478
|
+
}
|
|
7479
|
+
if (editQueue.items.length === 1 && ((_a = editQueue.items[0]) == null ? void 0 : _a.id) === lastEditQueueInfo.queueItemId) {
|
|
7480
|
+
return [];
|
|
7481
|
+
}
|
|
7482
|
+
return editQueue.items;
|
|
7483
|
+
}, [editQueue.items, lastEditQueueInfo, suppressQueueUntilResponse]);
|
|
7484
|
+
return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_react_native56.View, { style: [{ flex: 1 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_react_native56.View, { ref: captureTargetRef, style: { flex: 1 }, collapsable: false, children: [
|
|
7485
|
+
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
|
|
7050
7486
|
RuntimeRenderer,
|
|
7051
7487
|
{
|
|
7052
7488
|
appKey,
|
|
@@ -7056,7 +7492,7 @@ function ComergeStudioInner({
|
|
|
7056
7492
|
allowInitialPreparing: !embeddedBaseBundles
|
|
7057
7493
|
}
|
|
7058
7494
|
),
|
|
7059
|
-
/* @__PURE__ */ (0,
|
|
7495
|
+
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
|
|
7060
7496
|
StudioOverlay,
|
|
7061
7497
|
{
|
|
7062
7498
|
captureTargetRef,
|
|
@@ -7108,6 +7544,8 @@ function ComergeStudioInner({
|
|
|
7108
7544
|
chatSending: actions.sending,
|
|
7109
7545
|
chatShowTypingIndicator,
|
|
7110
7546
|
onSendChat: (text, attachments) => actions.sendEdit({ prompt: text, attachments }),
|
|
7547
|
+
chatQueueItems,
|
|
7548
|
+
onRemoveQueueItem: (id) => editQueueActions.cancel(id),
|
|
7111
7549
|
onNavigateHome,
|
|
7112
7550
|
showBubble,
|
|
7113
7551
|
studioControlOptions
|