@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.mjs CHANGED
@@ -6,8 +6,8 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  });
7
7
 
8
8
  // src/studio/ComergeStudio.tsx
9
- import * as React43 from "react";
10
- import { Platform as RNPlatform, View as View45 } from "react-native";
9
+ import * as React46 from "react";
10
+ import { Platform as RNPlatform, View as View46 } from "react-native";
11
11
  import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
12
12
 
13
13
  // src/studio/bootstrap/StudioBootstrap.tsx
@@ -816,6 +816,35 @@ function extractMeta(payload) {
816
816
  threadId: typeof obj.threadId === "string" ? obj.threadId : void 0
817
817
  };
818
818
  }
819
+ function getPayloadMeta(payload) {
820
+ const meta = payload == null ? void 0 : payload.meta;
821
+ if (!meta || typeof meta !== "object") return null;
822
+ return meta;
823
+ }
824
+ function isQueuedHiddenMessage(m) {
825
+ if (m.authorType !== "human") return false;
826
+ const meta = getPayloadMeta(m.payload);
827
+ return (meta == null ? void 0 : meta.visibility) === "queued";
828
+ }
829
+ function toEpochMs(value) {
830
+ if (value == null) return 0;
831
+ if (typeof value === "number") return value;
832
+ if (value instanceof Date) return value.getTime();
833
+ const parsed = Date.parse(String(value));
834
+ return Number.isFinite(parsed) ? parsed : 0;
835
+ }
836
+ function getEffectiveSortMs(m) {
837
+ const meta = getPayloadMeta(m.payload);
838
+ const runStartedAt = meta == null ? void 0 : meta.runStartedAt;
839
+ const runMs = toEpochMs(runStartedAt);
840
+ return runMs > 0 ? runMs : toEpochMs(m.createdAt);
841
+ }
842
+ function compareMessages(a, b) {
843
+ const aMs = getEffectiveSortMs(a);
844
+ const bMs = getEffectiveSortMs(b);
845
+ if (aMs !== bMs) return aMs - bMs;
846
+ return String(a.createdAt).localeCompare(String(b.createdAt));
847
+ }
819
848
  function mapMessageToChatMessage(m) {
820
849
  var _a, _b;
821
850
  const kind = typeof ((_a = m.payload) == null ? void 0 : _a.type) === "string" ? String(m.payload.type) : null;
@@ -835,8 +864,10 @@ function useThreadMessages(threadId) {
835
864
  const activeRequestIdRef = React4.useRef(0);
836
865
  const foregroundSignal = useForegroundSignal(Boolean(threadId));
837
866
  const upsertSorted = React4.useCallback((prev, m) => {
838
- const next = prev.some((x) => x.id === m.id) ? prev.map((x) => x.id === m.id ? m : x) : [...prev, m];
839
- next.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
867
+ const include = !isQueuedHiddenMessage(m);
868
+ const next = prev.filter((x) => x.id !== m.id);
869
+ if (include) next.push(m);
870
+ next.sort(compareMessages);
840
871
  return next;
841
872
  }, []);
842
873
  const refetch = React4.useCallback(async () => {
@@ -850,7 +881,7 @@ function useThreadMessages(threadId) {
850
881
  try {
851
882
  const list = await messagesRepository.list(threadId);
852
883
  if (activeRequestIdRef.current !== requestId) return;
853
- setRaw([...list].sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt))));
884
+ setRaw([...list].filter((m) => !isQueuedHiddenMessage(m)).sort(compareMessages));
854
885
  } catch (e) {
855
886
  if (activeRequestIdRef.current !== requestId) return;
856
887
  setError(e instanceof Error ? e : new Error(String(e)));
@@ -1902,6 +1933,9 @@ function useStudioActions({
1902
1933
  userId,
1903
1934
  app,
1904
1935
  onForkedApp,
1936
+ onEditStart,
1937
+ onEditQueued,
1938
+ onEditFinished,
1905
1939
  uploadAttachments
1906
1940
  }) {
1907
1941
  const [forking, setForking] = React8.useState(false);
@@ -1917,6 +1951,7 @@ function useStudioActions({
1917
1951
  setSending(true);
1918
1952
  setError(null);
1919
1953
  try {
1954
+ onEditStart == null ? void 0 : onEditStart();
1920
1955
  let targetApp = app;
1921
1956
  if (shouldForkOnEdit) {
1922
1957
  setForking(true);
@@ -1932,12 +1967,16 @@ function useStudioActions({
1932
1967
  if (attachments && attachments.length > 0 && uploadAttachments) {
1933
1968
  attachmentMetas = await uploadAttachments({ threadId, appId: targetApp.id, dataUrls: attachments });
1934
1969
  }
1935
- await agentRepository.editApp({
1970
+ const editResult = await agentRepository.editApp({
1936
1971
  prompt,
1937
1972
  thread_id: threadId,
1938
1973
  app_id: targetApp.id,
1939
1974
  attachments: attachmentMetas && attachmentMetas.length > 0 ? attachmentMetas : void 0
1940
1975
  });
1976
+ onEditQueued == null ? void 0 : onEditQueued({
1977
+ queueItemId: editResult.queueItemId ?? null,
1978
+ queuePosition: editResult.queuePosition ?? null
1979
+ });
1941
1980
  } catch (e) {
1942
1981
  const err = e instanceof Error ? e : new Error(String(e));
1943
1982
  setError(err);
@@ -1945,32 +1984,14 @@ function useStudioActions({
1945
1984
  } finally {
1946
1985
  setForking(false);
1947
1986
  setSending(false);
1987
+ onEditFinished == null ? void 0 : onEditFinished();
1948
1988
  }
1949
1989
  },
1950
- [app, onForkedApp, sending, shouldForkOnEdit, uploadAttachments, userId]
1990
+ [app, onEditFinished, onEditQueued, onEditStart, onForkedApp, sending, shouldForkOnEdit, uploadAttachments, userId]
1951
1991
  );
1952
1992
  return { isOwner, shouldForkOnEdit, forking, sending, error, sendEdit };
1953
1993
  }
1954
1994
 
1955
- // src/studio/lib/chat.ts
1956
- function hasNoOutcomeAfterLastHuman(messages) {
1957
- if (!messages || messages.length === 0) return false;
1958
- let lastHumanIndex = -1;
1959
- for (let i = messages.length - 1; i >= 0; i -= 1) {
1960
- if (messages[i].authorType === "human") {
1961
- lastHumanIndex = i;
1962
- break;
1963
- }
1964
- }
1965
- if (lastHumanIndex === -1) return false;
1966
- for (let i = lastHumanIndex + 1; i < messages.length; i += 1) {
1967
- const m = messages[i];
1968
- const payload = m.payload;
1969
- if (m.authorType === "ai" && (payload == null ? void 0 : payload.type) === "outcome") return false;
1970
- }
1971
- return true;
1972
- }
1973
-
1974
1995
  // src/studio/ui/RuntimeRenderer.tsx
1975
1996
  import * as React9 from "react";
1976
1997
  import { View as View2 } from "react-native";
@@ -2008,8 +2029,8 @@ function RuntimeRenderer({
2008
2029
  }
2009
2030
 
2010
2031
  // src/studio/ui/StudioOverlay.tsx
2011
- import * as React42 from "react";
2012
- import { Keyboard as Keyboard5, Platform as Platform10, View as View44, useWindowDimensions as useWindowDimensions4 } from "react-native";
2032
+ import * as React43 from "react";
2033
+ import { Keyboard as Keyboard5, Platform as Platform10, View as View45, useWindowDimensions as useWindowDimensions4 } from "react-native";
2013
2034
 
2014
2035
  // src/components/studio-sheet/StudioBottomSheet.tsx
2015
2036
  import * as React12 from "react";
@@ -5869,8 +5890,8 @@ function PreviewPanel({
5869
5890
  }
5870
5891
 
5871
5892
  // src/studio/ui/ChatPanel.tsx
5872
- import * as React39 from "react";
5873
- import { ActivityIndicator as ActivityIndicator8, View as View41 } from "react-native";
5893
+ import * as React40 from "react";
5894
+ import { ActivityIndicator as ActivityIndicator9, View as View42 } from "react-native";
5874
5895
 
5875
5896
  // src/components/chat/ChatPage.tsx
5876
5897
  import * as React37 from "react";
@@ -6067,6 +6088,7 @@ function ChatPage({
6067
6088
  showTypingIndicator,
6068
6089
  renderMessageContent,
6069
6090
  topBanner,
6091
+ composerTop,
6070
6092
  composer,
6071
6093
  overlay,
6072
6094
  style,
@@ -6077,6 +6099,7 @@ function ChatPage({
6077
6099
  const theme = useTheme();
6078
6100
  const insets = useSafeAreaInsets4();
6079
6101
  const [composerHeight, setComposerHeight] = React37.useState(0);
6102
+ const [composerTopHeight, setComposerTopHeight] = React37.useState(0);
6080
6103
  const [keyboardVisible, setKeyboardVisible] = React37.useState(false);
6081
6104
  React37.useEffect(() => {
6082
6105
  if (Platform9.OS !== "ios") return;
@@ -6088,8 +6111,9 @@ function ChatPage({
6088
6111
  };
6089
6112
  }, []);
6090
6113
  const footerBottomPadding = Platform9.OS === "ios" ? keyboardVisible ? 0 : insets.bottom : insets.bottom + 10;
6091
- const overlayBottom = composerHeight + footerBottomPadding + theme.spacing.lg;
6092
- const bottomInset = composerHeight + footerBottomPadding + theme.spacing.xl;
6114
+ const totalComposerHeight = composerHeight + composerTopHeight;
6115
+ const overlayBottom = totalComposerHeight + footerBottomPadding + theme.spacing.lg;
6116
+ const bottomInset = totalComposerHeight + footerBottomPadding + theme.spacing.xl;
6093
6117
  const resolvedOverlay = React37.useMemo(() => {
6094
6118
  var _a;
6095
6119
  if (!overlay) return null;
@@ -6099,6 +6123,10 @@ function ChatPage({
6099
6123
  style: [prevStyle, { bottom: overlayBottom }]
6100
6124
  });
6101
6125
  }, [overlay, overlayBottom]);
6126
+ React37.useEffect(() => {
6127
+ if (composerTop) return;
6128
+ setComposerTopHeight(0);
6129
+ }, [composerTop]);
6102
6130
  return /* @__PURE__ */ jsxs29(View37, { style: [{ flex: 1 }, style], children: [
6103
6131
  header ? /* @__PURE__ */ jsx49(View37, { children: header }) : null,
6104
6132
  topBanner ? /* @__PURE__ */ jsx49(View37, { style: { paddingHorizontal: theme.spacing.lg, paddingTop: theme.spacing.sm }, children: topBanner }) : null,
@@ -6123,7 +6151,7 @@ function ChatPage({
6123
6151
  ]
6124
6152
  }
6125
6153
  ),
6126
- /* @__PURE__ */ jsx49(
6154
+ /* @__PURE__ */ jsxs29(
6127
6155
  View37,
6128
6156
  {
6129
6157
  style: {
@@ -6135,14 +6163,24 @@ function ChatPage({
6135
6163
  paddingTop: theme.spacing.sm,
6136
6164
  paddingBottom: footerBottomPadding
6137
6165
  },
6138
- children: /* @__PURE__ */ jsx49(
6139
- ChatComposer,
6140
- {
6141
- ...composer,
6142
- attachments: composer.attachments ?? [],
6143
- onLayout: ({ height }) => setComposerHeight(height)
6144
- }
6145
- )
6166
+ children: [
6167
+ composerTop ? /* @__PURE__ */ jsx49(
6168
+ View37,
6169
+ {
6170
+ style: { marginBottom: theme.spacing.sm },
6171
+ onLayout: (e) => setComposerTopHeight(e.nativeEvent.layout.height),
6172
+ children: composerTop
6173
+ }
6174
+ ) : null,
6175
+ /* @__PURE__ */ jsx49(
6176
+ ChatComposer,
6177
+ {
6178
+ ...composer,
6179
+ attachments: composer.attachments ?? [],
6180
+ onLayout: ({ height }) => setComposerHeight(height)
6181
+ }
6182
+ )
6183
+ ]
6146
6184
  }
6147
6185
  )
6148
6186
  ] })
@@ -6292,8 +6330,154 @@ function ForkNoticeBanner({ isOwner = true, title, description, style }) {
6292
6330
  );
6293
6331
  }
6294
6332
 
6295
- // src/studio/ui/ChatPanel.tsx
6333
+ // src/components/chat/ChatQueue.tsx
6334
+ import * as React39 from "react";
6335
+ import { ActivityIndicator as ActivityIndicator8, Pressable as Pressable13, View as View41 } from "react-native";
6296
6336
  import { jsx as jsx53, jsxs as jsxs31 } from "react/jsx-runtime";
6337
+ function ChatQueue({ items, onRemove }) {
6338
+ const theme = useTheme();
6339
+ const [expanded, setExpanded] = React39.useState({});
6340
+ const [canExpand, setCanExpand] = React39.useState({});
6341
+ const [collapsedText, setCollapsedText] = React39.useState({});
6342
+ const [removing, setRemoving] = React39.useState({});
6343
+ const buildCollapsedText = React39.useCallback((lines) => {
6344
+ var _a, _b;
6345
+ const line1 = ((_a = lines[0]) == null ? void 0 : _a.text) ?? "";
6346
+ const line2 = ((_b = lines[1]) == null ? void 0 : _b.text) ?? "";
6347
+ const moreLabel = "more";
6348
+ const reserve = `\u2026 ${moreLabel}`.length;
6349
+ let trimmedLine2 = line2;
6350
+ if (trimmedLine2.length > reserve) {
6351
+ trimmedLine2 = trimmedLine2.slice(0, Math.max(0, trimmedLine2.length - reserve));
6352
+ } else {
6353
+ trimmedLine2 = "";
6354
+ }
6355
+ trimmedLine2 = trimmedLine2.replace(/\s+$/, "");
6356
+ return `${line1}
6357
+ ${trimmedLine2}\u2026 `;
6358
+ }, []);
6359
+ React39.useEffect(() => {
6360
+ if (items.length === 0) return;
6361
+ const ids = new Set(items.map((item) => item.id));
6362
+ setExpanded((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => ids.has(id))));
6363
+ setCanExpand((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => ids.has(id))));
6364
+ setCollapsedText((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => ids.has(id))));
6365
+ setRemoving((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => ids.has(id))));
6366
+ }, [items]);
6367
+ if (items.length === 0) return null;
6368
+ return /* @__PURE__ */ jsxs31(
6369
+ View41,
6370
+ {
6371
+ style: {
6372
+ borderWidth: 1,
6373
+ borderColor: theme.colors.border,
6374
+ borderRadius: theme.radii.lg,
6375
+ marginHorizontal: theme.spacing.md,
6376
+ padding: theme.spacing.md,
6377
+ backgroundColor: "transparent"
6378
+ },
6379
+ children: [
6380
+ /* @__PURE__ */ jsx53(Text, { variant: "caption", style: { marginBottom: theme.spacing.sm }, children: "Queue" }),
6381
+ /* @__PURE__ */ jsx53(View41, { style: { gap: theme.spacing.sm }, children: items.map((item) => {
6382
+ const isExpanded = Boolean(expanded[item.id]);
6383
+ const showToggle = Boolean(canExpand[item.id]);
6384
+ const prompt = item.prompt ?? "";
6385
+ const moreLabel = "more";
6386
+ const displayPrompt = !isExpanded && showToggle && collapsedText[item.id] ? collapsedText[item.id] : prompt;
6387
+ const isRemoving = Boolean(removing[item.id]);
6388
+ return /* @__PURE__ */ jsxs31(
6389
+ View41,
6390
+ {
6391
+ style: {
6392
+ flexDirection: "row",
6393
+ alignItems: "flex-start",
6394
+ gap: theme.spacing.sm,
6395
+ paddingHorizontal: theme.spacing.md,
6396
+ paddingVertical: theme.spacing.sm,
6397
+ borderRadius: theme.radii.md,
6398
+ backgroundColor: withAlpha(theme.colors.surface, theme.scheme === "dark" ? 0.8 : 0.9)
6399
+ },
6400
+ children: [
6401
+ /* @__PURE__ */ jsxs31(View41, { style: { flex: 1 }, children: [
6402
+ !canExpand[item.id] ? /* @__PURE__ */ jsx53(
6403
+ Text,
6404
+ {
6405
+ style: { position: "absolute", opacity: 0, zIndex: -1, width: "100%" },
6406
+ onTextLayout: (e) => {
6407
+ var _a;
6408
+ const lines = (_a = e.nativeEvent) == null ? void 0 : _a.lines;
6409
+ if (!lines) return;
6410
+ if (lines.length > 2) {
6411
+ setCanExpand((prev) => ({ ...prev, [item.id]: true }));
6412
+ setCollapsedText((prev) => ({
6413
+ ...prev,
6414
+ [item.id]: buildCollapsedText(lines)
6415
+ }));
6416
+ }
6417
+ },
6418
+ children: prompt
6419
+ }
6420
+ ) : null,
6421
+ /* @__PURE__ */ jsxs31(
6422
+ Text,
6423
+ {
6424
+ variant: "bodyMuted",
6425
+ numberOfLines: isExpanded ? void 0 : 2,
6426
+ children: [
6427
+ displayPrompt,
6428
+ !isExpanded && showToggle ? /* @__PURE__ */ jsx53(
6429
+ Text,
6430
+ {
6431
+ color: theme.colors.text,
6432
+ onPress: () => setExpanded((prev) => ({ ...prev, [item.id]: true })),
6433
+ suppressHighlighting: true,
6434
+ children: moreLabel
6435
+ }
6436
+ ) : null
6437
+ ]
6438
+ }
6439
+ ),
6440
+ showToggle && isExpanded ? /* @__PURE__ */ jsx53(
6441
+ Pressable13,
6442
+ {
6443
+ onPress: () => setExpanded((prev) => ({ ...prev, [item.id]: false })),
6444
+ hitSlop: 6,
6445
+ style: { alignSelf: "flex-start", marginTop: 4 },
6446
+ children: /* @__PURE__ */ jsx53(Text, { variant: "captionMuted", color: theme.colors.text, children: "less" })
6447
+ }
6448
+ ) : null
6449
+ ] }),
6450
+ /* @__PURE__ */ jsx53(
6451
+ Pressable13,
6452
+ {
6453
+ onPress: () => {
6454
+ if (!onRemove || isRemoving) return;
6455
+ setRemoving((prev) => ({ ...prev, [item.id]: true }));
6456
+ Promise.resolve(onRemove(item.id)).finally(() => {
6457
+ setRemoving((prev) => {
6458
+ if (!prev[item.id]) return prev;
6459
+ const { [item.id]: _removed, ...rest } = prev;
6460
+ return rest;
6461
+ });
6462
+ });
6463
+ },
6464
+ hitSlop: 8,
6465
+ style: { alignSelf: "center" },
6466
+ children: isRemoving ? /* @__PURE__ */ jsx53(ActivityIndicator8, { size: "small", color: theme.colors.text }) : /* @__PURE__ */ jsx53(IconClose, { size: 14, colorToken: "text" })
6467
+ }
6468
+ )
6469
+ ]
6470
+ },
6471
+ item.id
6472
+ );
6473
+ }) })
6474
+ ]
6475
+ }
6476
+ );
6477
+ }
6478
+
6479
+ // src/studio/ui/ChatPanel.tsx
6480
+ import { jsx as jsx54, jsxs as jsxs32 } from "react/jsx-runtime";
6297
6481
  function ChatPanel({
6298
6482
  title = "Chat",
6299
6483
  autoFocusComposer = false,
@@ -6311,11 +6495,13 @@ function ChatPanel({
6311
6495
  onClose,
6312
6496
  onNavigateHome,
6313
6497
  onStartDraw,
6314
- onSend
6498
+ onSend,
6499
+ queueItems = [],
6500
+ onRemoveQueueItem
6315
6501
  }) {
6316
- const listRef = React39.useRef(null);
6317
- const [nearBottom, setNearBottom] = React39.useState(true);
6318
- const handleSend = React39.useCallback(
6502
+ const listRef = React40.useRef(null);
6503
+ const [nearBottom, setNearBottom] = React40.useState(true);
6504
+ const handleSend = React40.useCallback(
6319
6505
  async (text, composerAttachments) => {
6320
6506
  const all = composerAttachments ?? attachments;
6321
6507
  await onSend(text, all.length > 0 ? all : void 0);
@@ -6329,25 +6515,25 @@ function ChatPanel({
6329
6515
  },
6330
6516
  [attachments, nearBottom, onClearAttachments, onSend]
6331
6517
  );
6332
- const handleScrollToBottom = React39.useCallback(() => {
6518
+ const handleScrollToBottom = React40.useCallback(() => {
6333
6519
  var _a;
6334
6520
  (_a = listRef.current) == null ? void 0 : _a.scrollToBottom({ animated: true });
6335
6521
  }, []);
6336
- const header = /* @__PURE__ */ jsx53(
6522
+ const header = /* @__PURE__ */ jsx54(
6337
6523
  ChatHeader,
6338
6524
  {
6339
- left: /* @__PURE__ */ jsxs31(View41, { style: { flexDirection: "row", alignItems: "center" }, children: [
6340
- /* @__PURE__ */ jsx53(StudioSheetHeaderIconButton, { onPress: onBack, accessibilityLabel: "Back", style: { marginRight: 8 }, children: /* @__PURE__ */ jsx53(IconBack, { size: 20, colorToken: "floatingContent" }) }),
6341
- onNavigateHome ? /* @__PURE__ */ jsx53(StudioSheetHeaderIconButton, { onPress: onNavigateHome, accessibilityLabel: "Home", children: /* @__PURE__ */ jsx53(IconHome, { size: 20, colorToken: "floatingContent" }) }) : null
6525
+ left: /* @__PURE__ */ jsxs32(View42, { style: { flexDirection: "row", alignItems: "center" }, children: [
6526
+ /* @__PURE__ */ jsx54(StudioSheetHeaderIconButton, { onPress: onBack, accessibilityLabel: "Back", style: { marginRight: 8 }, children: /* @__PURE__ */ jsx54(IconBack, { size: 20, colorToken: "floatingContent" }) }),
6527
+ onNavigateHome ? /* @__PURE__ */ jsx54(StudioSheetHeaderIconButton, { onPress: onNavigateHome, accessibilityLabel: "Home", children: /* @__PURE__ */ jsx54(IconHome, { size: 20, colorToken: "floatingContent" }) }) : null
6342
6528
  ] }),
6343
- right: /* @__PURE__ */ jsxs31(View41, { style: { flexDirection: "row", alignItems: "center" }, children: [
6344
- onStartDraw ? /* @__PURE__ */ jsx53(StudioSheetHeaderIconButton, { onPress: onStartDraw, accessibilityLabel: "Draw", intent: "danger", style: { marginRight: 8 }, children: /* @__PURE__ */ jsx53(IconDraw, { size: 20, colorToken: "onDanger" }) }) : null,
6345
- /* @__PURE__ */ jsx53(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", children: /* @__PURE__ */ jsx53(IconClose, { size: 20, colorToken: "floatingContent" }) })
6529
+ right: /* @__PURE__ */ jsxs32(View42, { style: { flexDirection: "row", alignItems: "center" }, children: [
6530
+ onStartDraw ? /* @__PURE__ */ jsx54(StudioSheetHeaderIconButton, { onPress: onStartDraw, accessibilityLabel: "Draw", intent: "danger", style: { marginRight: 8 }, children: /* @__PURE__ */ jsx54(IconDraw, { size: 20, colorToken: "onDanger" }) }) : null,
6531
+ /* @__PURE__ */ jsx54(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", children: /* @__PURE__ */ jsx54(IconClose, { size: 20, colorToken: "floatingContent" }) })
6346
6532
  ] }),
6347
6533
  center: null
6348
6534
  }
6349
6535
  );
6350
- const topBanner = shouldForkOnEdit ? /* @__PURE__ */ jsx53(
6536
+ const topBanner = shouldForkOnEdit ? /* @__PURE__ */ jsx54(
6351
6537
  ForkNoticeBanner,
6352
6538
  {
6353
6539
  isOwner: !shouldForkOnEdit,
@@ -6356,33 +6542,35 @@ function ChatPanel({
6356
6542
  ) : null;
6357
6543
  const showMessagesLoading = Boolean(loading) && messages.length === 0 || forking;
6358
6544
  if (showMessagesLoading) {
6359
- return /* @__PURE__ */ jsxs31(View41, { style: { flex: 1 }, children: [
6360
- /* @__PURE__ */ jsx53(View41, { children: header }),
6361
- topBanner ? /* @__PURE__ */ jsx53(View41, { style: { paddingHorizontal: 16, paddingTop: 8 }, children: topBanner }) : null,
6362
- /* @__PURE__ */ jsxs31(View41, { style: { flex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 24, paddingVertical: 12 }, children: [
6363
- /* @__PURE__ */ jsx53(ActivityIndicator8, {}),
6364
- /* @__PURE__ */ jsx53(View41, { style: { height: 12 } }),
6365
- /* @__PURE__ */ jsx53(Text, { variant: "bodyMuted", children: forking ? "Creating your copy\u2026" : "Loading messages\u2026" })
6545
+ return /* @__PURE__ */ jsxs32(View42, { style: { flex: 1 }, children: [
6546
+ /* @__PURE__ */ jsx54(View42, { children: header }),
6547
+ topBanner ? /* @__PURE__ */ jsx54(View42, { style: { paddingHorizontal: 16, paddingTop: 8 }, children: topBanner }) : null,
6548
+ /* @__PURE__ */ jsxs32(View42, { style: { flex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 24, paddingVertical: 12 }, children: [
6549
+ /* @__PURE__ */ jsx54(ActivityIndicator9, {}),
6550
+ /* @__PURE__ */ jsx54(View42, { style: { height: 12 } }),
6551
+ /* @__PURE__ */ jsx54(Text, { variant: "bodyMuted", children: forking ? "Creating your copy\u2026" : "Loading messages\u2026" })
6366
6552
  ] })
6367
6553
  ] });
6368
6554
  }
6369
- return /* @__PURE__ */ jsx53(
6555
+ const queueTop = queueItems.length > 0 ? /* @__PURE__ */ jsx54(ChatQueue, { items: queueItems, onRemove: onRemoveQueueItem }) : null;
6556
+ return /* @__PURE__ */ jsx54(
6370
6557
  ChatPage,
6371
6558
  {
6372
6559
  header,
6373
6560
  messages,
6374
6561
  showTypingIndicator,
6375
6562
  topBanner,
6563
+ composerTop: queueTop,
6376
6564
  composerHorizontalPadding: 0,
6377
6565
  listRef,
6378
6566
  onNearBottomChange: setNearBottom,
6379
- overlay: /* @__PURE__ */ jsx53(
6567
+ overlay: /* @__PURE__ */ jsx54(
6380
6568
  ScrollToBottomButton,
6381
6569
  {
6382
6570
  visible: !nearBottom,
6383
6571
  onPress: handleScrollToBottom,
6384
6572
  style: { bottom: 80 },
6385
- children: /* @__PURE__ */ jsx53(IconArrowDown, { size: 20, colorToken: "floatingContent" })
6573
+ children: /* @__PURE__ */ jsx54(IconArrowDown, { size: 20, colorToken: "floatingContent" })
6386
6574
  }
6387
6575
  ),
6388
6576
  composer: {
@@ -6403,16 +6591,16 @@ function ChatPanel({
6403
6591
  }
6404
6592
 
6405
6593
  // src/components/dialogs/ConfirmMergeRequestDialog.tsx
6406
- import * as React40 from "react";
6407
- import { Pressable as Pressable14, View as View43 } from "react-native";
6594
+ import * as React41 from "react";
6595
+ import { Pressable as Pressable15, View as View44 } from "react-native";
6408
6596
 
6409
6597
  // src/components/primitives/Modal.tsx
6410
6598
  import {
6411
6599
  Modal as RNModal,
6412
- Pressable as Pressable13,
6413
- View as View42
6600
+ Pressable as Pressable14,
6601
+ View as View43
6414
6602
  } from "react-native";
6415
- import { jsx as jsx54, jsxs as jsxs32 } from "react/jsx-runtime";
6603
+ import { jsx as jsx55, jsxs as jsxs33 } from "react/jsx-runtime";
6416
6604
  function Modal({
6417
6605
  visible,
6418
6606
  onRequestClose,
@@ -6421,30 +6609,30 @@ function Modal({
6421
6609
  contentStyle
6422
6610
  }) {
6423
6611
  const theme = useTheme();
6424
- return /* @__PURE__ */ jsx54(
6612
+ return /* @__PURE__ */ jsx55(
6425
6613
  RNModal,
6426
6614
  {
6427
6615
  visible,
6428
6616
  transparent: true,
6429
6617
  animationType: "fade",
6430
6618
  onRequestClose,
6431
- children: /* @__PURE__ */ jsxs32(View42, { style: { flex: 1, backgroundColor: theme.colors.backdrop, justifyContent: "center", padding: theme.spacing.lg }, children: [
6432
- /* @__PURE__ */ jsx54(
6433
- Pressable13,
6619
+ children: /* @__PURE__ */ jsxs33(View43, { style: { flex: 1, backgroundColor: theme.colors.backdrop, justifyContent: "center", padding: theme.spacing.lg }, children: [
6620
+ /* @__PURE__ */ jsx55(
6621
+ Pressable14,
6434
6622
  {
6435
6623
  accessibilityRole: "button",
6436
6624
  onPress: dismissOnBackdropPress ? onRequestClose : void 0,
6437
6625
  style: { position: "absolute", inset: 0 }
6438
6626
  }
6439
6627
  ),
6440
- /* @__PURE__ */ jsx54(Card, { variant: "surfaceRaised", padded: true, style: [{ borderRadius: theme.radii.xl }, contentStyle], children })
6628
+ /* @__PURE__ */ jsx55(Card, { variant: "surfaceRaised", padded: true, style: [{ borderRadius: theme.radii.xl }, contentStyle], children })
6441
6629
  ] })
6442
6630
  }
6443
6631
  );
6444
6632
  }
6445
6633
 
6446
6634
  // src/components/dialogs/ConfirmMergeRequestDialog.tsx
6447
- import { jsx as jsx55, jsxs as jsxs33 } from "react/jsx-runtime";
6635
+ import { jsx as jsx56, jsxs as jsxs34 } from "react/jsx-runtime";
6448
6636
  function ConfirmMergeRequestDialog({
6449
6637
  visible,
6450
6638
  onOpenChange,
@@ -6455,14 +6643,14 @@ function ConfirmMergeRequestDialog({
6455
6643
  onTestFirst
6456
6644
  }) {
6457
6645
  const theme = useTheme();
6458
- const close = React40.useCallback(() => onOpenChange(false), [onOpenChange]);
6646
+ const close = React41.useCallback(() => onOpenChange(false), [onOpenChange]);
6459
6647
  const canConfirm = Boolean(mergeRequest) && !approveDisabled;
6460
- const handleConfirm = React40.useCallback(() => {
6648
+ const handleConfirm = React41.useCallback(() => {
6461
6649
  if (!mergeRequest) return;
6462
6650
  onOpenChange(false);
6463
6651
  void onConfirm();
6464
6652
  }, [mergeRequest, onConfirm, onOpenChange]);
6465
- const handleTestFirst = React40.useCallback(() => {
6653
+ const handleTestFirst = React41.useCallback(() => {
6466
6654
  if (!mergeRequest) return;
6467
6655
  onOpenChange(false);
6468
6656
  void onTestFirst(mergeRequest);
@@ -6474,7 +6662,7 @@ function ConfirmMergeRequestDialog({
6474
6662
  justifyContent: "center",
6475
6663
  alignSelf: "stretch"
6476
6664
  };
6477
- return /* @__PURE__ */ jsxs33(
6665
+ return /* @__PURE__ */ jsxs34(
6478
6666
  Modal,
6479
6667
  {
6480
6668
  visible,
@@ -6485,7 +6673,7 @@ function ConfirmMergeRequestDialog({
6485
6673
  backgroundColor: theme.colors.background
6486
6674
  },
6487
6675
  children: [
6488
- /* @__PURE__ */ jsx55(View43, { children: /* @__PURE__ */ jsx55(
6676
+ /* @__PURE__ */ jsx56(View44, { children: /* @__PURE__ */ jsx56(
6489
6677
  Text,
6490
6678
  {
6491
6679
  style: {
@@ -6497,9 +6685,9 @@ function ConfirmMergeRequestDialog({
6497
6685
  children: "Are you sure you want to approve this merge request?"
6498
6686
  }
6499
6687
  ) }),
6500
- /* @__PURE__ */ jsxs33(View43, { style: { marginTop: 16 }, children: [
6501
- /* @__PURE__ */ jsx55(
6502
- View43,
6688
+ /* @__PURE__ */ jsxs34(View44, { style: { marginTop: 16 }, children: [
6689
+ /* @__PURE__ */ jsx56(
6690
+ View44,
6503
6691
  {
6504
6692
  style: [
6505
6693
  fullWidthButtonBase,
@@ -6508,22 +6696,22 @@ function ConfirmMergeRequestDialog({
6508
6696
  opacity: canConfirm ? 1 : 0.5
6509
6697
  }
6510
6698
  ],
6511
- children: /* @__PURE__ */ jsx55(
6512
- Pressable14,
6699
+ children: /* @__PURE__ */ jsx56(
6700
+ Pressable15,
6513
6701
  {
6514
6702
  accessibilityRole: "button",
6515
6703
  accessibilityLabel: "Approve Merge",
6516
6704
  disabled: !canConfirm,
6517
6705
  onPress: handleConfirm,
6518
6706
  style: [fullWidthButtonBase, { flex: 1 }],
6519
- children: /* @__PURE__ */ jsx55(Text, { style: { textAlign: "center", color: theme.colors.onPrimary }, children: "Approve Merge" })
6707
+ children: /* @__PURE__ */ jsx56(Text, { style: { textAlign: "center", color: theme.colors.onPrimary }, children: "Approve Merge" })
6520
6708
  }
6521
6709
  )
6522
6710
  }
6523
6711
  ),
6524
- /* @__PURE__ */ jsx55(View43, { style: { height: 8 } }),
6525
- /* @__PURE__ */ jsx55(
6526
- View43,
6712
+ /* @__PURE__ */ jsx56(View44, { style: { height: 8 } }),
6713
+ /* @__PURE__ */ jsx56(
6714
+ View44,
6527
6715
  {
6528
6716
  style: [
6529
6717
  fullWidthButtonBase,
@@ -6534,22 +6722,22 @@ function ConfirmMergeRequestDialog({
6534
6722
  opacity: isBuilding || !mergeRequest ? 0.5 : 1
6535
6723
  }
6536
6724
  ],
6537
- children: /* @__PURE__ */ jsx55(
6538
- Pressable14,
6725
+ children: /* @__PURE__ */ jsx56(
6726
+ Pressable15,
6539
6727
  {
6540
6728
  accessibilityRole: "button",
6541
6729
  accessibilityLabel: isBuilding ? "Preparing\u2026" : "Test edits first",
6542
6730
  disabled: isBuilding || !mergeRequest,
6543
6731
  onPress: handleTestFirst,
6544
6732
  style: [fullWidthButtonBase, { flex: 1 }],
6545
- children: /* @__PURE__ */ jsx55(Text, { style: { textAlign: "center", color: theme.colors.text }, children: isBuilding ? "Preparing\u2026" : "Test edits first" })
6733
+ children: /* @__PURE__ */ jsx56(Text, { style: { textAlign: "center", color: theme.colors.text }, children: isBuilding ? "Preparing\u2026" : "Test edits first" })
6546
6734
  }
6547
6735
  )
6548
6736
  }
6549
6737
  ),
6550
- /* @__PURE__ */ jsx55(View43, { style: { height: 8 } }),
6551
- /* @__PURE__ */ jsx55(
6552
- View43,
6738
+ /* @__PURE__ */ jsx56(View44, { style: { height: 8 } }),
6739
+ /* @__PURE__ */ jsx56(
6740
+ View44,
6553
6741
  {
6554
6742
  style: [
6555
6743
  fullWidthButtonBase,
@@ -6559,14 +6747,14 @@ function ConfirmMergeRequestDialog({
6559
6747
  borderColor: theme.colors.border
6560
6748
  }
6561
6749
  ],
6562
- children: /* @__PURE__ */ jsx55(
6563
- Pressable14,
6750
+ children: /* @__PURE__ */ jsx56(
6751
+ Pressable15,
6564
6752
  {
6565
6753
  accessibilityRole: "button",
6566
6754
  accessibilityLabel: "Cancel",
6567
6755
  onPress: close,
6568
6756
  style: [fullWidthButtonBase, { flex: 1 }],
6569
- children: /* @__PURE__ */ jsx55(Text, { style: { textAlign: "center", color: theme.colors.text }, children: "Cancel" })
6757
+ children: /* @__PURE__ */ jsx56(Text, { style: { textAlign: "center", color: theme.colors.text }, children: "Cancel" })
6570
6758
  }
6571
6759
  )
6572
6760
  }
@@ -6578,7 +6766,7 @@ function ConfirmMergeRequestDialog({
6578
6766
  }
6579
6767
 
6580
6768
  // src/studio/ui/ConfirmMergeFlow.tsx
6581
- import { jsx as jsx56 } from "react/jsx-runtime";
6769
+ import { jsx as jsx57 } from "react/jsx-runtime";
6582
6770
  function ConfirmMergeFlow({
6583
6771
  visible,
6584
6772
  onOpenChange,
@@ -6589,7 +6777,7 @@ function ConfirmMergeFlow({
6589
6777
  onConfirm,
6590
6778
  onTestFirst
6591
6779
  }) {
6592
- return /* @__PURE__ */ jsx56(
6780
+ return /* @__PURE__ */ jsx57(
6593
6781
  ConfirmMergeRequestDialog,
6594
6782
  {
6595
6783
  visible,
@@ -6611,11 +6799,11 @@ function ConfirmMergeFlow({
6611
6799
  }
6612
6800
 
6613
6801
  // src/studio/hooks/useOptimisticChatMessages.ts
6614
- import * as React41 from "react";
6802
+ import * as React42 from "react";
6615
6803
  function makeOptimisticId() {
6616
6804
  return `optimistic:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 10)}`;
6617
6805
  }
6618
- function toEpochMs(createdAt) {
6806
+ function toEpochMs2(createdAt) {
6619
6807
  if (createdAt == null) return 0;
6620
6808
  if (typeof createdAt === "number") return createdAt;
6621
6809
  if (createdAt instanceof Date) return createdAt.getTime();
@@ -6634,7 +6822,7 @@ function isOptimisticResolvedByServer(chatMessages, o) {
6634
6822
  for (const m of candidates) {
6635
6823
  if (m.author !== "human") continue;
6636
6824
  if (normalize(m.content) !== target) continue;
6637
- const serverMs = toEpochMs(m.createdAt);
6825
+ const serverMs = toEpochMs2(m.createdAt);
6638
6826
  const optimisticMs = Date.parse(o.createdAtIso);
6639
6827
  if (Number.isFinite(optimisticMs) && optimisticMs > 0 && serverMs > 0) {
6640
6828
  if (serverMs + 12e4 < optimisticMs) continue;
@@ -6646,14 +6834,15 @@ function isOptimisticResolvedByServer(chatMessages, o) {
6646
6834
  function useOptimisticChatMessages({
6647
6835
  threadId,
6648
6836
  shouldForkOnEdit,
6837
+ disableOptimistic = false,
6649
6838
  chatMessages,
6650
6839
  onSendChat
6651
6840
  }) {
6652
- const [optimisticChat, setOptimisticChat] = React41.useState([]);
6653
- React41.useEffect(() => {
6841
+ const [optimisticChat, setOptimisticChat] = React42.useState([]);
6842
+ React42.useEffect(() => {
6654
6843
  setOptimisticChat([]);
6655
6844
  }, [threadId]);
6656
- const messages = React41.useMemo(() => {
6845
+ const messages = React42.useMemo(() => {
6657
6846
  if (!optimisticChat || optimisticChat.length === 0) return chatMessages;
6658
6847
  const unresolved = optimisticChat.filter((o) => !isOptimisticResolvedByServer(chatMessages, o));
6659
6848
  if (unresolved.length === 0) return chatMessages;
@@ -6669,7 +6858,7 @@ function useOptimisticChatMessages({
6669
6858
  merged.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
6670
6859
  return merged;
6671
6860
  }, [chatMessages, optimisticChat]);
6672
- React41.useEffect(() => {
6861
+ React42.useEffect(() => {
6673
6862
  if (optimisticChat.length === 0) return;
6674
6863
  setOptimisticChat((prev) => {
6675
6864
  if (prev.length === 0) return prev;
@@ -6677,9 +6866,9 @@ function useOptimisticChatMessages({
6677
6866
  return next.length === prev.length ? prev : next;
6678
6867
  });
6679
6868
  }, [chatMessages, optimisticChat.length]);
6680
- const onSend = React41.useCallback(
6869
+ const onSend = React42.useCallback(
6681
6870
  async (text, attachments) => {
6682
- if (shouldForkOnEdit) {
6871
+ if (shouldForkOnEdit || disableOptimistic) {
6683
6872
  await onSendChat(text, attachments);
6684
6873
  return;
6685
6874
  }
@@ -6691,7 +6880,7 @@ function useOptimisticChatMessages({
6691
6880
  setOptimisticChat((prev) => prev.map((m) => m.id === id ? { ...m, failed: true } : m));
6692
6881
  });
6693
6882
  },
6694
- [chatMessages, onSendChat, shouldForkOnEdit]
6883
+ [chatMessages, disableOptimistic, onSendChat, shouldForkOnEdit]
6695
6884
  );
6696
6885
  return { messages, onSend };
6697
6886
  }
@@ -6701,7 +6890,7 @@ import {
6701
6890
  publishComergeStudioUIState,
6702
6891
  startStudioControlPolling
6703
6892
  } from "@comergehq/studio-control";
6704
- import { Fragment as Fragment6, jsx as jsx57, jsxs as jsxs34 } from "react/jsx-runtime";
6893
+ import { Fragment as Fragment6, jsx as jsx58, jsxs as jsxs35 } from "react/jsx-runtime";
6705
6894
  function StudioOverlay({
6706
6895
  captureTargetRef,
6707
6896
  app,
@@ -6728,44 +6917,48 @@ function StudioOverlay({
6728
6917
  chatSending,
6729
6918
  chatShowTypingIndicator,
6730
6919
  onSendChat,
6920
+ chatQueueItems,
6921
+ onRemoveQueueItem,
6731
6922
  onNavigateHome,
6732
6923
  showBubble,
6733
6924
  studioControlOptions
6734
6925
  }) {
6735
6926
  const theme = useTheme();
6736
6927
  const { width } = useWindowDimensions4();
6737
- const [sheetOpen, setSheetOpen] = React42.useState(false);
6738
- const sheetOpenRef = React42.useRef(sheetOpen);
6739
- const [activePage, setActivePage] = React42.useState("preview");
6740
- const [drawing, setDrawing] = React42.useState(false);
6741
- const [chatAttachments, setChatAttachments] = React42.useState([]);
6742
- const [commentsAppId, setCommentsAppId] = React42.useState(null);
6743
- const [commentsCount, setCommentsCount] = React42.useState(null);
6928
+ const [sheetOpen, setSheetOpen] = React43.useState(false);
6929
+ const sheetOpenRef = React43.useRef(sheetOpen);
6930
+ const [activePage, setActivePage] = React43.useState("preview");
6931
+ const [drawing, setDrawing] = React43.useState(false);
6932
+ const [chatAttachments, setChatAttachments] = React43.useState([]);
6933
+ const [commentsAppId, setCommentsAppId] = React43.useState(null);
6934
+ const [commentsCount, setCommentsCount] = React43.useState(null);
6744
6935
  const threadId = (app == null ? void 0 : app.threadId) ?? null;
6936
+ const disableOptimistic = Boolean(chatQueueItems && chatQueueItems.length > 0) || (app == null ? void 0 : app.status) === "editing";
6745
6937
  const optimistic = useOptimisticChatMessages({
6746
6938
  threadId,
6747
6939
  shouldForkOnEdit,
6940
+ disableOptimistic,
6748
6941
  chatMessages,
6749
6942
  onSendChat
6750
6943
  });
6751
- const [confirmMrId, setConfirmMrId] = React42.useState(null);
6752
- const confirmMr = React42.useMemo(
6944
+ const [confirmMrId, setConfirmMrId] = React43.useState(null);
6945
+ const confirmMr = React43.useMemo(
6753
6946
  () => confirmMrId ? incomingMergeRequests.find((m) => m.id === confirmMrId) ?? null : null,
6754
6947
  [confirmMrId, incomingMergeRequests]
6755
6948
  );
6756
- const handleSheetOpenChange = React42.useCallback((open) => {
6949
+ const handleSheetOpenChange = React43.useCallback((open) => {
6757
6950
  setSheetOpen(open);
6758
6951
  if (!open) Keyboard5.dismiss();
6759
6952
  }, []);
6760
- const closeSheet = React42.useCallback(() => {
6953
+ const closeSheet = React43.useCallback(() => {
6761
6954
  handleSheetOpenChange(false);
6762
6955
  }, [handleSheetOpenChange]);
6763
- const openSheet = React42.useCallback(() => setSheetOpen(true), []);
6764
- const goToChat = React42.useCallback(() => {
6956
+ const openSheet = React43.useCallback(() => setSheetOpen(true), []);
6957
+ const goToChat = React43.useCallback(() => {
6765
6958
  setActivePage("chat");
6766
6959
  openSheet();
6767
6960
  }, [openSheet]);
6768
- const backToPreview = React42.useCallback(() => {
6961
+ const backToPreview = React43.useCallback(() => {
6769
6962
  if (Platform10.OS !== "ios") {
6770
6963
  Keyboard5.dismiss();
6771
6964
  setActivePage("preview");
@@ -6783,11 +6976,11 @@ function StudioOverlay({
6783
6976
  const t = setTimeout(finalize, 350);
6784
6977
  Keyboard5.dismiss();
6785
6978
  }, []);
6786
- const startDraw = React42.useCallback(() => {
6979
+ const startDraw = React43.useCallback(() => {
6787
6980
  setDrawing(true);
6788
6981
  closeSheet();
6789
6982
  }, [closeSheet]);
6790
- const handleDrawCapture = React42.useCallback(
6983
+ const handleDrawCapture = React43.useCallback(
6791
6984
  (dataUrl) => {
6792
6985
  setChatAttachments((prev) => [...prev, dataUrl]);
6793
6986
  setDrawing(false);
@@ -6796,7 +6989,7 @@ function StudioOverlay({
6796
6989
  },
6797
6990
  [openSheet]
6798
6991
  );
6799
- const toggleSheet = React42.useCallback(async () => {
6992
+ const toggleSheet = React43.useCallback(async () => {
6800
6993
  if (!sheetOpen) {
6801
6994
  const shouldExitTest = Boolean(testingMrId) || isTesting;
6802
6995
  if (shouldExitTest) {
@@ -6808,7 +7001,7 @@ function StudioOverlay({
6808
7001
  closeSheet();
6809
7002
  }
6810
7003
  }, [closeSheet, isTesting, onRestoreBase, sheetOpen, testingMrId]);
6811
- const handleTestMr = React42.useCallback(
7004
+ const handleTestMr = React43.useCallback(
6812
7005
  async (mr) => {
6813
7006
  if (!onTestMr) return;
6814
7007
  await onTestMr(mr);
@@ -6816,10 +7009,10 @@ function StudioOverlay({
6816
7009
  },
6817
7010
  [closeSheet, onTestMr]
6818
7011
  );
6819
- React42.useEffect(() => {
7012
+ React43.useEffect(() => {
6820
7013
  sheetOpenRef.current = sheetOpen;
6821
7014
  }, [sheetOpen]);
6822
- React42.useEffect(() => {
7015
+ React43.useEffect(() => {
6823
7016
  const poller = startStudioControlPolling((action) => {
6824
7017
  if (action === "show" && !sheetOpenRef.current) openSheet();
6825
7018
  if (action === "hide" && sheetOpenRef.current) closeSheet();
@@ -6827,17 +7020,17 @@ function StudioOverlay({
6827
7020
  }, studioControlOptions);
6828
7021
  return () => poller.stop();
6829
7022
  }, [closeSheet, openSheet, studioControlOptions, toggleSheet]);
6830
- React42.useEffect(() => {
7023
+ React43.useEffect(() => {
6831
7024
  void publishComergeStudioUIState(sheetOpen, studioControlOptions);
6832
7025
  }, [sheetOpen, studioControlOptions]);
6833
- return /* @__PURE__ */ jsxs34(Fragment6, { children: [
6834
- /* @__PURE__ */ jsx57(EdgeGlowFrame, { visible: isTesting, role: "accent", thickness: 40, intensity: 1 }),
6835
- /* @__PURE__ */ jsx57(StudioBottomSheet, { open: sheetOpen, onOpenChange: handleSheetOpenChange, children: /* @__PURE__ */ jsx57(
7026
+ return /* @__PURE__ */ jsxs35(Fragment6, { children: [
7027
+ /* @__PURE__ */ jsx58(EdgeGlowFrame, { visible: isTesting, role: "accent", thickness: 40, intensity: 1 }),
7028
+ /* @__PURE__ */ jsx58(StudioBottomSheet, { open: sheetOpen, onOpenChange: handleSheetOpenChange, children: /* @__PURE__ */ jsx58(
6836
7029
  StudioSheetPager,
6837
7030
  {
6838
7031
  activePage,
6839
7032
  width,
6840
- preview: /* @__PURE__ */ jsx57(
7033
+ preview: /* @__PURE__ */ jsx58(
6841
7034
  PreviewPanel,
6842
7035
  {
6843
7036
  app,
@@ -6863,7 +7056,7 @@ function StudioOverlay({
6863
7056
  commentCountOverride: commentsCount ?? void 0
6864
7057
  }
6865
7058
  ),
6866
- chat: /* @__PURE__ */ jsx57(
7059
+ chat: /* @__PURE__ */ jsx58(
6867
7060
  ChatPanel,
6868
7061
  {
6869
7062
  messages: optimistic.messages,
@@ -6881,12 +7074,14 @@ function StudioOverlay({
6881
7074
  onClose: closeSheet,
6882
7075
  onNavigateHome,
6883
7076
  onStartDraw: startDraw,
6884
- onSend: optimistic.onSend
7077
+ onSend: optimistic.onSend,
7078
+ queueItems: chatQueueItems,
7079
+ onRemoveQueueItem
6885
7080
  }
6886
7081
  )
6887
7082
  }
6888
7083
  ) }),
6889
- showBubble && /* @__PURE__ */ jsx57(
7084
+ showBubble && /* @__PURE__ */ jsx58(
6890
7085
  Bubble,
6891
7086
  {
6892
7087
  visible: !sheetOpen && !drawing,
@@ -6894,10 +7089,10 @@ function StudioOverlay({
6894
7089
  badgeCount: incomingMergeRequests.length,
6895
7090
  onPress: toggleSheet,
6896
7091
  isLoading: (app == null ? void 0 : app.status) === "editing",
6897
- children: /* @__PURE__ */ jsx57(View44, { style: { width: 28, height: 28, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx57(MergeIcon, { width: 24, height: 24, color: theme.colors.floatingContent }) })
7092
+ children: /* @__PURE__ */ jsx58(View45, { style: { width: 28, height: 28, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsx58(MergeIcon, { width: 24, height: 24, color: theme.colors.floatingContent }) })
6898
7093
  }
6899
7094
  ),
6900
- /* @__PURE__ */ jsx57(
7095
+ /* @__PURE__ */ jsx58(
6901
7096
  DrawModeOverlay,
6902
7097
  {
6903
7098
  visible: drawing,
@@ -6906,7 +7101,7 @@ function StudioOverlay({
6906
7101
  onCapture: handleDrawCapture
6907
7102
  }
6908
7103
  ),
6909
- /* @__PURE__ */ jsx57(
7104
+ /* @__PURE__ */ jsx58(
6910
7105
  ConfirmMergeFlow,
6911
7106
  {
6912
7107
  visible: Boolean(confirmMr),
@@ -6919,7 +7114,7 @@ function StudioOverlay({
6919
7114
  onTestFirst: handleTestMr
6920
7115
  }
6921
7116
  ),
6922
- /* @__PURE__ */ jsx57(
7117
+ /* @__PURE__ */ jsx58(
6923
7118
  AppCommentsSheet,
6924
7119
  {
6925
7120
  appId: commentsAppId,
@@ -6931,8 +7126,190 @@ function StudioOverlay({
6931
7126
  ] });
6932
7127
  }
6933
7128
 
7129
+ // src/studio/hooks/useEditQueue.ts
7130
+ import * as React44 from "react";
7131
+
7132
+ // src/data/apps/edit-queue/remote.ts
7133
+ var EditQueueRemoteDataSourceImpl = class extends BaseRemote {
7134
+ async list(appId) {
7135
+ const { data } = await api.get(
7136
+ `/v1/apps/${encodeURIComponent(appId)}/edit-queue`
7137
+ );
7138
+ return data;
7139
+ }
7140
+ async update(appId, queueItemId, payload) {
7141
+ const { data } = await api.patch(
7142
+ `/v1/apps/${encodeURIComponent(appId)}/edit-queue/${encodeURIComponent(queueItemId)}`,
7143
+ payload
7144
+ );
7145
+ return data;
7146
+ }
7147
+ async cancel(appId, queueItemId) {
7148
+ const { data } = await api.delete(
7149
+ `/v1/apps/${encodeURIComponent(appId)}/edit-queue/${encodeURIComponent(queueItemId)}`
7150
+ );
7151
+ return data;
7152
+ }
7153
+ };
7154
+ var editQueueRemoteDataSource = new EditQueueRemoteDataSourceImpl();
7155
+
7156
+ // src/data/apps/edit-queue/repository.ts
7157
+ var ACTIVE_STATUSES = ["pending"];
7158
+ function toString(value) {
7159
+ return typeof value === "string" && value.trim().length > 0 ? value : null;
7160
+ }
7161
+ function toAttachments(value) {
7162
+ return Array.isArray(value) ? value : [];
7163
+ }
7164
+ function mapQueueItem(row) {
7165
+ const payload = row.payload ?? {};
7166
+ return {
7167
+ id: row.id,
7168
+ status: row.status,
7169
+ prompt: toString(payload.trimmedPrompt),
7170
+ messageId: toString(payload.messageId),
7171
+ attachments: toAttachments(payload.attachments),
7172
+ createdAt: row.created_at,
7173
+ updatedAt: row.updated_at,
7174
+ runAfter: row.run_after,
7175
+ priority: row.priority
7176
+ };
7177
+ }
7178
+ var EditQueueRepositoryImpl = class extends BaseRepository {
7179
+ constructor(remote) {
7180
+ super();
7181
+ this.remote = remote;
7182
+ }
7183
+ async list(appId) {
7184
+ const res = await this.remote.list(appId);
7185
+ const data = this.unwrapOrThrow(res);
7186
+ return data.items ?? [];
7187
+ }
7188
+ async update(appId, queueItemId, payload) {
7189
+ const res = await this.remote.update(appId, queueItemId, payload);
7190
+ return this.unwrapOrThrow(res);
7191
+ }
7192
+ async cancel(appId, queueItemId) {
7193
+ const res = await this.remote.cancel(appId, queueItemId);
7194
+ return this.unwrapOrThrow(res);
7195
+ }
7196
+ subscribeEditQueue(appId, handlers) {
7197
+ const supabase = getSupabaseClient();
7198
+ const channel = supabase.channel(`edit-queue:app:${appId}`).on(
7199
+ "postgres_changes",
7200
+ { event: "INSERT", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
7201
+ (payload) => {
7202
+ var _a;
7203
+ const row = payload.new;
7204
+ if (row.kind !== "edit") return;
7205
+ const item = mapQueueItem(row);
7206
+ if (!ACTIVE_STATUSES.includes(item.status)) return;
7207
+ (_a = handlers.onInsert) == null ? void 0 : _a.call(handlers, item);
7208
+ }
7209
+ ).on(
7210
+ "postgres_changes",
7211
+ { event: "UPDATE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
7212
+ (payload) => {
7213
+ var _a, _b;
7214
+ const row = payload.new;
7215
+ if (row.kind !== "edit") return;
7216
+ const item = mapQueueItem(row);
7217
+ if (ACTIVE_STATUSES.includes(item.status)) (_a = handlers.onUpdate) == null ? void 0 : _a.call(handlers, item);
7218
+ else (_b = handlers.onDelete) == null ? void 0 : _b.call(handlers, item);
7219
+ }
7220
+ ).on(
7221
+ "postgres_changes",
7222
+ { event: "DELETE", schema: "public", table: "app_job_queue", filter: `app_id=eq.${appId}` },
7223
+ (payload) => {
7224
+ var _a;
7225
+ const row = payload.old;
7226
+ if (row.kind !== "edit") return;
7227
+ (_a = handlers.onDelete) == null ? void 0 : _a.call(handlers, mapQueueItem(row));
7228
+ }
7229
+ ).subscribe();
7230
+ return () => {
7231
+ supabase.removeChannel(channel);
7232
+ };
7233
+ }
7234
+ };
7235
+ var editQueueRepository = new EditQueueRepositoryImpl(
7236
+ editQueueRemoteDataSource
7237
+ );
7238
+
7239
+ // src/studio/hooks/useEditQueue.ts
7240
+ function useEditQueue(appId) {
7241
+ const [items, setItems] = React44.useState([]);
7242
+ const [loading, setLoading] = React44.useState(false);
7243
+ const [error, setError] = React44.useState(null);
7244
+ const activeRequestIdRef = React44.useRef(0);
7245
+ const foregroundSignal = useForegroundSignal(Boolean(appId));
7246
+ const upsertSorted = React44.useCallback((prev, nextItem) => {
7247
+ const next = prev.some((x) => x.id === nextItem.id) ? prev.map((x) => x.id === nextItem.id ? nextItem : x) : [...prev, nextItem];
7248
+ next.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
7249
+ return next;
7250
+ }, []);
7251
+ const refetch = React44.useCallback(async () => {
7252
+ if (!appId) {
7253
+ setItems([]);
7254
+ return;
7255
+ }
7256
+ const requestId = ++activeRequestIdRef.current;
7257
+ setLoading(true);
7258
+ setError(null);
7259
+ try {
7260
+ const list = await editQueueRepository.list(appId);
7261
+ if (activeRequestIdRef.current !== requestId) return;
7262
+ setItems([...list].sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt))));
7263
+ } catch (e) {
7264
+ if (activeRequestIdRef.current !== requestId) return;
7265
+ setError(e instanceof Error ? e : new Error(String(e)));
7266
+ setItems([]);
7267
+ } finally {
7268
+ if (activeRequestIdRef.current === requestId) setLoading(false);
7269
+ }
7270
+ }, [appId]);
7271
+ React44.useEffect(() => {
7272
+ void refetch();
7273
+ }, [refetch]);
7274
+ React44.useEffect(() => {
7275
+ if (!appId) return;
7276
+ const unsubscribe = editQueueRepository.subscribeEditQueue(appId, {
7277
+ onInsert: (item) => setItems((prev) => upsertSorted(prev, item)),
7278
+ onUpdate: (item) => setItems((prev) => upsertSorted(prev, item)),
7279
+ onDelete: (item) => setItems((prev) => prev.filter((x) => x.id !== item.id))
7280
+ });
7281
+ return unsubscribe;
7282
+ }, [appId, upsertSorted, foregroundSignal]);
7283
+ React44.useEffect(() => {
7284
+ if (!appId) return;
7285
+ if (foregroundSignal <= 0) return;
7286
+ void refetch();
7287
+ }, [appId, foregroundSignal, refetch]);
7288
+ return { items, loading, error, refetch };
7289
+ }
7290
+
7291
+ // src/studio/hooks/useEditQueueActions.ts
7292
+ import * as React45 from "react";
7293
+ function useEditQueueActions(appId) {
7294
+ const update = React45.useCallback(
7295
+ async (queueItemId, payload) => {
7296
+ if (!appId) return;
7297
+ await editQueueRepository.update(appId, queueItemId, payload);
7298
+ },
7299
+ [appId]
7300
+ );
7301
+ const cancel = React45.useCallback(
7302
+ async (queueItemId) => {
7303
+ if (!appId) return;
7304
+ await editQueueRepository.cancel(appId, queueItemId);
7305
+ },
7306
+ [appId]
7307
+ );
7308
+ return { update, cancel };
7309
+ }
7310
+
6934
7311
  // src/studio/ComergeStudio.tsx
6935
- import { jsx as jsx58, jsxs as jsxs35 } from "react/jsx-runtime";
7312
+ import { jsx as jsx59, jsxs as jsxs36 } from "react/jsx-runtime";
6936
7313
  function ComergeStudio({
6937
7314
  appId,
6938
7315
  clientKey: clientKey2,
@@ -6943,17 +7320,17 @@ function ComergeStudio({
6943
7320
  studioControlOptions,
6944
7321
  embeddedBaseBundles
6945
7322
  }) {
6946
- const [activeAppId, setActiveAppId] = React43.useState(appId);
6947
- const [runtimeAppId, setRuntimeAppId] = React43.useState(appId);
6948
- const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React43.useState(null);
6949
- const platform = React43.useMemo(() => RNPlatform.OS === "ios" ? "ios" : "android", []);
6950
- React43.useEffect(() => {
7323
+ const [activeAppId, setActiveAppId] = React46.useState(appId);
7324
+ const [runtimeAppId, setRuntimeAppId] = React46.useState(appId);
7325
+ const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React46.useState(null);
7326
+ const platform = React46.useMemo(() => RNPlatform.OS === "ios" ? "ios" : "android", []);
7327
+ React46.useEffect(() => {
6951
7328
  setActiveAppId(appId);
6952
7329
  setRuntimeAppId(appId);
6953
7330
  setPendingRuntimeTargetAppId(null);
6954
7331
  }, [appId]);
6955
- const captureTargetRef = React43.useRef(null);
6956
- return /* @__PURE__ */ jsx58(StudioBootstrap, { clientKey: clientKey2, fallback: /* @__PURE__ */ jsx58(View45, { style: { flex: 1 } }), children: ({ userId }) => /* @__PURE__ */ jsx58(BottomSheetModalProvider, { children: /* @__PURE__ */ jsx58(LiquidGlassResetProvider, { resetTriggers: [appId, activeAppId, runtimeAppId], children: /* @__PURE__ */ jsx58(
7332
+ const captureTargetRef = React46.useRef(null);
7333
+ return /* @__PURE__ */ jsx59(StudioBootstrap, { clientKey: clientKey2, fallback: /* @__PURE__ */ jsx59(View46, { style: { flex: 1 } }), children: ({ userId }) => /* @__PURE__ */ jsx59(BottomSheetModalProvider, { children: /* @__PURE__ */ jsx59(LiquidGlassResetProvider, { resetTriggers: [appId, activeAppId, runtimeAppId], children: /* @__PURE__ */ jsx59(
6957
7334
  ComergeStudioInner,
6958
7335
  {
6959
7336
  userId,
@@ -6994,11 +7371,11 @@ function ComergeStudioInner({
6994
7371
  const { app, loading: appLoading } = useApp(activeAppId);
6995
7372
  const { app: runtimeAppFromHook } = useApp(runtimeAppId, { enabled: runtimeAppId !== activeAppId });
6996
7373
  const runtimeApp = runtimeAppId === activeAppId ? app : runtimeAppFromHook;
6997
- const sawEditingOnPendingTargetRef = React43.useRef(false);
6998
- React43.useEffect(() => {
7374
+ const sawEditingOnPendingTargetRef = React46.useRef(false);
7375
+ React46.useEffect(() => {
6999
7376
  sawEditingOnPendingTargetRef.current = false;
7000
7377
  }, [pendingRuntimeTargetAppId]);
7001
- React43.useEffect(() => {
7378
+ React46.useEffect(() => {
7002
7379
  if (!pendingRuntimeTargetAppId) return;
7003
7380
  if (activeAppId !== pendingRuntimeTargetAppId) return;
7004
7381
  if ((app == null ? void 0 : app.status) === "editing") {
@@ -7016,13 +7393,13 @@ function ComergeStudioInner({
7016
7393
  canRequestLatest: (runtimeApp == null ? void 0 : runtimeApp.status) === "ready",
7017
7394
  embeddedBaseBundles
7018
7395
  });
7019
- const sawEditingOnActiveAppRef = React43.useRef(false);
7020
- const [showPostEditPreparing, setShowPostEditPreparing] = React43.useState(false);
7021
- React43.useEffect(() => {
7396
+ const sawEditingOnActiveAppRef = React46.useRef(false);
7397
+ const [showPostEditPreparing, setShowPostEditPreparing] = React46.useState(false);
7398
+ React46.useEffect(() => {
7022
7399
  sawEditingOnActiveAppRef.current = false;
7023
7400
  setShowPostEditPreparing(false);
7024
7401
  }, [activeAppId]);
7025
- React43.useEffect(() => {
7402
+ React46.useEffect(() => {
7026
7403
  if (!(app == null ? void 0 : app.id)) return;
7027
7404
  if (app.status === "editing") {
7028
7405
  sawEditingOnActiveAppRef.current = true;
@@ -7034,7 +7411,7 @@ function ComergeStudioInner({
7034
7411
  sawEditingOnActiveAppRef.current = false;
7035
7412
  }
7036
7413
  }, [app == null ? void 0 : app.id, app == null ? void 0 : app.status]);
7037
- React43.useEffect(() => {
7414
+ React46.useEffect(() => {
7038
7415
  if (!showPostEditPreparing) return;
7039
7416
  const stillProcessingBaseBundle = bundle.loading && bundle.loadingMode === "base" && !bundle.isTesting;
7040
7417
  if (!stillProcessingBaseBundle) {
@@ -7043,15 +7420,27 @@ function ComergeStudioInner({
7043
7420
  }, [showPostEditPreparing, bundle.loading, bundle.loadingMode, bundle.isTesting]);
7044
7421
  const threadId = (app == null ? void 0 : app.threadId) ?? "";
7045
7422
  const thread = useThreadMessages(threadId);
7423
+ const editQueue = useEditQueue(activeAppId);
7424
+ const editQueueActions = useEditQueueActions(activeAppId);
7425
+ const [lastEditQueueInfo, setLastEditQueueInfo] = React46.useState(null);
7426
+ const lastEditQueueInfoRef = React46.useRef(null);
7427
+ const [suppressQueueUntilResponse, setSuppressQueueUntilResponse] = React46.useState(false);
7046
7428
  const mergeRequests = useMergeRequests({ appId: activeAppId });
7047
- const hasOpenOutgoingMr = React43.useMemo(() => {
7429
+ const hasOpenOutgoingMr = React46.useMemo(() => {
7048
7430
  return mergeRequests.lists.outgoing.some((mr) => mr.status === "open");
7049
7431
  }, [mergeRequests.lists.outgoing]);
7050
- const incomingReviewMrs = React43.useMemo(() => {
7432
+ const incomingReviewMrs = React46.useMemo(() => {
7051
7433
  if (!userId) return mergeRequests.lists.incoming;
7052
7434
  return mergeRequests.lists.incoming.filter((mr) => mr.createdBy !== userId);
7053
7435
  }, [mergeRequests.lists.incoming, userId]);
7054
7436
  const uploader = useAttachmentUpload();
7437
+ const updateLastEditQueueInfo = React46.useCallback(
7438
+ (info) => {
7439
+ lastEditQueueInfoRef.current = info;
7440
+ setLastEditQueueInfo(info);
7441
+ },
7442
+ []
7443
+ );
7055
7444
  const actions = useStudioActions({
7056
7445
  userId,
7057
7446
  app,
@@ -7066,20 +7455,62 @@ function ComergeStudioInner({
7066
7455
  setPendingRuntimeTargetAppId(null);
7067
7456
  }
7068
7457
  },
7069
- uploadAttachments: uploader.uploadBase64Images
7458
+ uploadAttachments: uploader.uploadBase64Images,
7459
+ onEditStart: () => {
7460
+ if (editQueue.items.length === 0) {
7461
+ setSuppressQueueUntilResponse(true);
7462
+ }
7463
+ },
7464
+ onEditQueued: (info) => {
7465
+ updateLastEditQueueInfo(info);
7466
+ if (info.queuePosition !== 1) {
7467
+ setSuppressQueueUntilResponse(false);
7468
+ }
7469
+ },
7470
+ onEditFinished: () => {
7471
+ var _a;
7472
+ if (((_a = lastEditQueueInfoRef.current) == null ? void 0 : _a.queuePosition) !== 1) {
7473
+ setSuppressQueueUntilResponse(false);
7474
+ }
7475
+ }
7070
7476
  });
7071
- const chatSendDisabled = hasNoOutcomeAfterLastHuman(thread.raw);
7072
- const [processingMrId, setProcessingMrId] = React43.useState(null);
7073
- const [testingMrId, setTestingMrId] = React43.useState(null);
7074
- const chatShowTypingIndicator = React43.useMemo(() => {
7477
+ const chatSendDisabled = false;
7478
+ const [processingMrId, setProcessingMrId] = React46.useState(null);
7479
+ const [testingMrId, setTestingMrId] = React46.useState(null);
7480
+ const chatShowTypingIndicator = React46.useMemo(() => {
7075
7481
  var _a;
7076
7482
  if (!thread.raw || thread.raw.length === 0) return false;
7077
7483
  const last = thread.raw[thread.raw.length - 1];
7078
7484
  const payloadType = typeof ((_a = last.payload) == null ? void 0 : _a.type) === "string" ? String(last.payload.type) : void 0;
7079
7485
  return payloadType !== "outcome";
7080
7486
  }, [thread.raw]);
7081
- return /* @__PURE__ */ jsx58(View45, { style: [{ flex: 1 }, style], children: /* @__PURE__ */ jsxs35(View45, { ref: captureTargetRef, style: { flex: 1 }, collapsable: false, children: [
7082
- /* @__PURE__ */ jsx58(
7487
+ React46.useEffect(() => {
7488
+ updateLastEditQueueInfo(null);
7489
+ setSuppressQueueUntilResponse(false);
7490
+ }, [activeAppId, updateLastEditQueueInfo]);
7491
+ React46.useEffect(() => {
7492
+ if (!(lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId)) return;
7493
+ const stillPresent = editQueue.items.some((item) => item.id === lastEditQueueInfo.queueItemId);
7494
+ if (!stillPresent) {
7495
+ updateLastEditQueueInfo(null);
7496
+ setSuppressQueueUntilResponse(false);
7497
+ }
7498
+ }, [editQueue.items, lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId]);
7499
+ const chatQueueItems = React46.useMemo(() => {
7500
+ var _a;
7501
+ if (suppressQueueUntilResponse && editQueue.items.length <= 1) {
7502
+ return [];
7503
+ }
7504
+ if (!lastEditQueueInfo || lastEditQueueInfo.queuePosition !== 1 || !lastEditQueueInfo.queueItemId) {
7505
+ return editQueue.items;
7506
+ }
7507
+ if (editQueue.items.length === 1 && ((_a = editQueue.items[0]) == null ? void 0 : _a.id) === lastEditQueueInfo.queueItemId) {
7508
+ return [];
7509
+ }
7510
+ return editQueue.items;
7511
+ }, [editQueue.items, lastEditQueueInfo, suppressQueueUntilResponse]);
7512
+ return /* @__PURE__ */ jsx59(View46, { style: [{ flex: 1 }, style], children: /* @__PURE__ */ jsxs36(View46, { ref: captureTargetRef, style: { flex: 1 }, collapsable: false, children: [
7513
+ /* @__PURE__ */ jsx59(
7083
7514
  RuntimeRenderer,
7084
7515
  {
7085
7516
  appKey,
@@ -7089,7 +7520,7 @@ function ComergeStudioInner({
7089
7520
  allowInitialPreparing: !embeddedBaseBundles
7090
7521
  }
7091
7522
  ),
7092
- /* @__PURE__ */ jsx58(
7523
+ /* @__PURE__ */ jsx59(
7093
7524
  StudioOverlay,
7094
7525
  {
7095
7526
  captureTargetRef,
@@ -7141,6 +7572,8 @@ function ComergeStudioInner({
7141
7572
  chatSending: actions.sending,
7142
7573
  chatShowTypingIndicator,
7143
7574
  onSendChat: (text, attachments) => actions.sendEdit({ prompt: text, attachments }),
7575
+ chatQueueItems,
7576
+ onRemoveQueueItem: (id) => editQueueActions.cancel(id),
7144
7577
  onNavigateHome,
7145
7578
  showBubble,
7146
7579
  studioControlOptions