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