@comergehq/studio 0.1.16 → 0.1.18

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,7 +6,7 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  });
7
7
 
8
8
  // src/studio/ComergeStudio.tsx
9
- import * as React46 from "react";
9
+ import * as React47 from "react";
10
10
  import { Platform as RNPlatform, View as View46 } from "react-native";
11
11
  import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
12
12
 
@@ -864,9 +864,8 @@ function useThreadMessages(threadId) {
864
864
  const activeRequestIdRef = React4.useRef(0);
865
865
  const foregroundSignal = useForegroundSignal(Boolean(threadId));
866
866
  const upsertSorted = React4.useCallback((prev, m) => {
867
- const include = !isQueuedHiddenMessage(m);
868
867
  const next = prev.filter((x) => x.id !== m.id);
869
- if (include) next.push(m);
868
+ next.push(m);
870
869
  next.sort(compareMessages);
871
870
  return next;
872
871
  }, []);
@@ -881,7 +880,7 @@ function useThreadMessages(threadId) {
881
880
  try {
882
881
  const list = await messagesRepository.list(threadId);
883
882
  if (activeRequestIdRef.current !== requestId) return;
884
- setRaw([...list].filter((m) => !isQueuedHiddenMessage(m)).sort(compareMessages));
883
+ setRaw([...list].sort(compareMessages));
885
884
  } catch (e) {
886
885
  if (activeRequestIdRef.current !== requestId) return;
887
886
  setError(e instanceof Error ? e : new Error(String(e)));
@@ -907,7 +906,11 @@ function useThreadMessages(threadId) {
907
906
  if (foregroundSignal <= 0) return;
908
907
  void refetch();
909
908
  }, [foregroundSignal, refetch, threadId]);
910
- const messages = React4.useMemo(() => raw.map(mapMessageToChatMessage), [raw]);
909
+ const messages = React4.useMemo(() => {
910
+ const visible = raw.filter((m) => !isQueuedHiddenMessage(m));
911
+ const resolved = visible.length > 0 ? visible : raw;
912
+ return resolved.map(mapMessageToChatMessage);
913
+ }, [raw]);
911
914
  return { raw, messages, loading, error, refetch };
912
915
  }
913
916
 
@@ -1951,7 +1954,6 @@ function useStudioActions({
1951
1954
  setSending(true);
1952
1955
  setError(null);
1953
1956
  try {
1954
- onEditStart == null ? void 0 : onEditStart();
1955
1957
  let targetApp = app;
1956
1958
  if (shouldForkOnEdit) {
1957
1959
  setForking(true);
@@ -1963,6 +1965,7 @@ function useStudioActions({
1963
1965
  setForking(false);
1964
1966
  const threadId = targetApp.threadId;
1965
1967
  if (!threadId) throw new Error("No thread available for this app.");
1968
+ onEditStart == null ? void 0 : onEditStart();
1966
1969
  let attachmentMetas;
1967
1970
  if (attachments && attachments.length > 0 && uploadAttachments) {
1968
1971
  attachmentMetas = await uploadAttachments({ threadId, appId: targetApp.id, dataUrls: attachments });
@@ -2029,8 +2032,8 @@ function RuntimeRenderer({
2029
2032
  }
2030
2033
 
2031
2034
  // src/studio/ui/StudioOverlay.tsx
2032
- import * as React43 from "react";
2033
- import { Keyboard as Keyboard5, Platform as Platform10, View as View45, useWindowDimensions as useWindowDimensions4 } from "react-native";
2035
+ import * as React44 from "react";
2036
+ import { Keyboard as Keyboard5, Platform as Platform11, View as View45, useWindowDimensions as useWindowDimensions4 } from "react-native";
2034
2037
 
2035
2038
  // src/components/studio-sheet/StudioBottomSheet.tsx
2036
2039
  import * as React12 from "react";
@@ -3227,7 +3230,8 @@ import {
3227
3230
  Play,
3228
3231
  Send,
3229
3232
  X as X2,
3230
- Check as Check2
3233
+ Check as Check2,
3234
+ Share2
3231
3235
  } from "lucide-react-native";
3232
3236
  import { jsx as jsx16 } from "react/jsx-runtime";
3233
3237
  function useResolvedIconColor(token) {
@@ -3267,6 +3271,7 @@ var IconSend = makeIcon(Send);
3267
3271
  var IconPlay = makeIcon(Play);
3268
3272
  var IconArrowDown = makeIcon(ArrowDown);
3269
3273
  var IconApprove = makeIcon(Check2);
3274
+ var IconShare = makeIcon(Share2);
3270
3275
 
3271
3276
  // src/components/chat/ChatComposer.tsx
3272
3277
  import { jsx as jsx17, jsxs as jsxs8 } from "react/jsx-runtime";
@@ -3983,7 +3988,8 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3983
3988
  }
3984
3989
 
3985
3990
  // src/studio/ui/PreviewPanel.tsx
3986
- import { ActivityIndicator as ActivityIndicator7, View as View33 } from "react-native";
3991
+ import * as React35 from "react";
3992
+ import { ActivityIndicator as ActivityIndicator7, Platform as Platform8, Share, View as View33 } from "react-native";
3987
3993
 
3988
3994
  // src/components/preview/PreviewPage.tsx
3989
3995
  import { View as View15 } from "react-native";
@@ -4133,7 +4139,14 @@ function StudioSheetHeaderIconButton({
4133
4139
 
4134
4140
  // src/studio/ui/preview-panel/PreviewPanelHeader.tsx
4135
4141
  import { jsx as jsx24, jsxs as jsxs13 } from "react/jsx-runtime";
4136
- function PreviewPanelHeader({ isOwner, onClose, onNavigateHome, onGoToChat }) {
4142
+ function PreviewPanelHeader({
4143
+ isOwner,
4144
+ isPublic,
4145
+ onClose,
4146
+ onNavigateHome,
4147
+ onGoToChat,
4148
+ onShare
4149
+ }) {
4137
4150
  return /* @__PURE__ */ jsx24(
4138
4151
  StudioSheetHeader,
4139
4152
  {
@@ -4151,6 +4164,17 @@ function PreviewPanelHeader({ isOwner, onClose, onNavigateHome, onGoToChat }) {
4151
4164
  children: /* @__PURE__ */ jsx24(IconChat, { size: 20, colorToken: "onPrimary" })
4152
4165
  }
4153
4166
  ) : null,
4167
+ isPublic && onShare ? /* @__PURE__ */ jsx24(
4168
+ StudioSheetHeaderIconButton,
4169
+ {
4170
+ onPress: onShare,
4171
+ accessibilityLabel: "Share",
4172
+ intent: "primary",
4173
+ appearance: "glass",
4174
+ style: { marginRight: 8 },
4175
+ children: /* @__PURE__ */ jsx24(IconShare, { size: 20, colorToken: "onPrimary" })
4176
+ }
4177
+ ) : null,
4154
4178
  /* @__PURE__ */ jsx24(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", appearance: "glass", intent: "primary", children: /* @__PURE__ */ jsx24(IconClose, { size: 20, colorToken: "onPrimary" }) })
4155
4179
  ] })
4156
4180
  }
@@ -5823,6 +5847,27 @@ function PreviewPanel({
5823
5847
  onOpenComments,
5824
5848
  commentCountOverride
5825
5849
  }) {
5850
+ const handleShare = React35.useCallback(async () => {
5851
+ if (!app || !app.isPublic) return;
5852
+ const shareUrl = `https://comerge.ai/app/${app.id}`;
5853
+ const message = app.name ? `${app.name} on Comerge
5854
+ ${shareUrl}` : `Check out this app on Comerge
5855
+ ${shareUrl}`;
5856
+ try {
5857
+ const title = app.name ?? "Comerge app";
5858
+ const payload = Platform8.OS === "ios" ? {
5859
+ title,
5860
+ message
5861
+ } : {
5862
+ title,
5863
+ message,
5864
+ url: shareUrl
5865
+ };
5866
+ await Share.share(payload);
5867
+ } catch (error) {
5868
+ log.warn("PreviewPanel share failed", error);
5869
+ }
5870
+ }, [app]);
5826
5871
  const { imageUrl, imageLoaded, setImageLoaded, creator, insights, stats, showProcessing, canSubmitMergeRequest } = usePreviewPanelData({
5827
5872
  app,
5828
5873
  isOwner,
@@ -5830,7 +5875,17 @@ function PreviewPanel({
5830
5875
  onOpenComments,
5831
5876
  commentCountOverride
5832
5877
  });
5833
- const header = /* @__PURE__ */ jsx45(PreviewPanelHeader, { isOwner, onClose, onNavigateHome, onGoToChat });
5878
+ const header = /* @__PURE__ */ jsx45(
5879
+ PreviewPanelHeader,
5880
+ {
5881
+ isOwner,
5882
+ isPublic: Boolean(app == null ? void 0 : app.isPublic),
5883
+ onClose,
5884
+ onNavigateHome,
5885
+ onGoToChat,
5886
+ onShare: handleShare
5887
+ }
5888
+ );
5834
5889
  if (loading || !app) {
5835
5890
  return /* @__PURE__ */ jsx45(PreviewPage, { header, children: /* @__PURE__ */ jsxs26(View33, { style: { flex: 1, justifyContent: "center", alignItems: "center", padding: 24 }, children: [
5836
5891
  /* @__PURE__ */ jsx45(ActivityIndicator7, {}),
@@ -5890,16 +5945,16 @@ function PreviewPanel({
5890
5945
  }
5891
5946
 
5892
5947
  // src/studio/ui/ChatPanel.tsx
5893
- import * as React40 from "react";
5948
+ import * as React41 from "react";
5894
5949
  import { ActivityIndicator as ActivityIndicator9, View as View42 } from "react-native";
5895
5950
 
5896
5951
  // src/components/chat/ChatPage.tsx
5897
- import * as React37 from "react";
5898
- import { Keyboard as Keyboard4, Platform as Platform9, View as View37 } from "react-native";
5952
+ import * as React38 from "react";
5953
+ import { Keyboard as Keyboard4, Platform as Platform10, View as View37 } from "react-native";
5899
5954
  import { useSafeAreaInsets as useSafeAreaInsets4 } from "react-native-safe-area-context";
5900
5955
 
5901
5956
  // src/components/chat/ChatMessageList.tsx
5902
- import * as React36 from "react";
5957
+ import * as React37 from "react";
5903
5958
  import { View as View36 } from "react-native";
5904
5959
  import { BottomSheetFlatList } from "@gorhom/bottom-sheet";
5905
5960
 
@@ -5945,17 +6000,17 @@ function ChatMessageBubble({ message, renderContent, style }) {
5945
6000
  }
5946
6001
 
5947
6002
  // src/components/chat/TypingIndicator.tsx
5948
- import * as React35 from "react";
6003
+ import * as React36 from "react";
5949
6004
  import { Animated as Animated10, View as View35 } from "react-native";
5950
6005
  import { jsx as jsx47 } from "react/jsx-runtime";
5951
6006
  function TypingIndicator({ style }) {
5952
6007
  const theme = useTheme();
5953
6008
  const dotColor = theme.colors.textSubtle;
5954
- const anims = React35.useMemo(
6009
+ const anims = React36.useMemo(
5955
6010
  () => [new Animated10.Value(0.3), new Animated10.Value(0.3), new Animated10.Value(0.3)],
5956
6011
  []
5957
6012
  );
5958
- React35.useEffect(() => {
6013
+ React36.useEffect(() => {
5959
6014
  const loops = [];
5960
6015
  anims.forEach((a, idx) => {
5961
6016
  const seq = Animated10.sequence([
@@ -5989,7 +6044,7 @@ function TypingIndicator({ style }) {
5989
6044
 
5990
6045
  // src/components/chat/ChatMessageList.tsx
5991
6046
  import { jsx as jsx48, jsxs as jsxs28 } from "react/jsx-runtime";
5992
- var ChatMessageList = React36.forwardRef(
6047
+ var ChatMessageList = React37.forwardRef(
5993
6048
  ({
5994
6049
  messages,
5995
6050
  showTypingIndicator = false,
@@ -6000,20 +6055,20 @@ var ChatMessageList = React36.forwardRef(
6000
6055
  nearBottomThreshold = 200
6001
6056
  }, ref) => {
6002
6057
  const theme = useTheme();
6003
- const listRef = React36.useRef(null);
6004
- const nearBottomRef = React36.useRef(true);
6005
- const initialScrollDoneRef = React36.useRef(false);
6006
- const lastMessageIdRef = React36.useRef(null);
6007
- const data = React36.useMemo(() => {
6058
+ const listRef = React37.useRef(null);
6059
+ const nearBottomRef = React37.useRef(true);
6060
+ const initialScrollDoneRef = React37.useRef(false);
6061
+ const lastMessageIdRef = React37.useRef(null);
6062
+ const data = React37.useMemo(() => {
6008
6063
  return [...messages].reverse();
6009
6064
  }, [messages]);
6010
- const scrollToBottom = React36.useCallback((options) => {
6065
+ const scrollToBottom = React37.useCallback((options) => {
6011
6066
  var _a;
6012
6067
  const animated = (options == null ? void 0 : options.animated) ?? true;
6013
6068
  (_a = listRef.current) == null ? void 0 : _a.scrollToOffset({ offset: 0, animated });
6014
6069
  }, []);
6015
- React36.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
6016
- const handleScroll = React36.useCallback(
6070
+ React37.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
6071
+ const handleScroll = React37.useCallback(
6017
6072
  (e) => {
6018
6073
  const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;
6019
6074
  const distanceFromBottom = Math.max(contentOffset.y - Math.max(bottomInset, 0), 0);
@@ -6025,7 +6080,7 @@ var ChatMessageList = React36.forwardRef(
6025
6080
  },
6026
6081
  [bottomInset, nearBottomThreshold, onNearBottomChange]
6027
6082
  );
6028
- React36.useEffect(() => {
6083
+ React37.useEffect(() => {
6029
6084
  if (!initialScrollDoneRef.current) return;
6030
6085
  const lastId = messages.length > 0 ? messages[messages.length - 1].id : null;
6031
6086
  const prevLastId = lastMessageIdRef.current;
@@ -6035,7 +6090,7 @@ var ChatMessageList = React36.forwardRef(
6035
6090
  const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
6036
6091
  return () => cancelAnimationFrame(id);
6037
6092
  }, [messages, scrollToBottom]);
6038
- React36.useEffect(() => {
6093
+ React37.useEffect(() => {
6039
6094
  if (showTypingIndicator && nearBottomRef.current) {
6040
6095
  const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
6041
6096
  return () => cancelAnimationFrame(id);
@@ -6098,11 +6153,11 @@ function ChatPage({
6098
6153
  }) {
6099
6154
  const theme = useTheme();
6100
6155
  const insets = useSafeAreaInsets4();
6101
- const [composerHeight, setComposerHeight] = React37.useState(0);
6102
- const [composerTopHeight, setComposerTopHeight] = React37.useState(0);
6103
- const [keyboardVisible, setKeyboardVisible] = React37.useState(false);
6104
- React37.useEffect(() => {
6105
- if (Platform9.OS !== "ios") return;
6156
+ const [composerHeight, setComposerHeight] = React38.useState(0);
6157
+ const [composerTopHeight, setComposerTopHeight] = React38.useState(0);
6158
+ const [keyboardVisible, setKeyboardVisible] = React38.useState(false);
6159
+ React38.useEffect(() => {
6160
+ if (Platform10.OS !== "ios") return;
6106
6161
  const show = Keyboard4.addListener("keyboardWillShow", () => setKeyboardVisible(true));
6107
6162
  const hide = Keyboard4.addListener("keyboardWillHide", () => setKeyboardVisible(false));
6108
6163
  return () => {
@@ -6110,20 +6165,20 @@ function ChatPage({
6110
6165
  hide.remove();
6111
6166
  };
6112
6167
  }, []);
6113
- const footerBottomPadding = Platform9.OS === "ios" ? keyboardVisible ? 0 : insets.bottom : insets.bottom + 10;
6168
+ const footerBottomPadding = Platform10.OS === "ios" ? keyboardVisible ? 0 : insets.bottom : insets.bottom + 10;
6114
6169
  const totalComposerHeight = composerHeight + composerTopHeight;
6115
6170
  const overlayBottom = totalComposerHeight + footerBottomPadding + theme.spacing.lg;
6116
6171
  const bottomInset = totalComposerHeight + footerBottomPadding + theme.spacing.xl;
6117
- const resolvedOverlay = React37.useMemo(() => {
6172
+ const resolvedOverlay = React38.useMemo(() => {
6118
6173
  var _a;
6119
6174
  if (!overlay) return null;
6120
- if (!React37.isValidElement(overlay)) return overlay;
6175
+ if (!React38.isValidElement(overlay)) return overlay;
6121
6176
  const prevStyle = (_a = overlay.props) == null ? void 0 : _a.style;
6122
- return React37.cloneElement(overlay, {
6177
+ return React38.cloneElement(overlay, {
6123
6178
  style: [prevStyle, { bottom: overlayBottom }]
6124
6179
  });
6125
6180
  }, [overlay, overlayBottom]);
6126
- React37.useEffect(() => {
6181
+ React38.useEffect(() => {
6127
6182
  if (composerTop) return;
6128
6183
  setComposerTopHeight(0);
6129
6184
  }, [composerTop]);
@@ -6188,15 +6243,15 @@ function ChatPage({
6188
6243
  }
6189
6244
 
6190
6245
  // src/components/chat/ScrollToBottomButton.tsx
6191
- import * as React38 from "react";
6246
+ import * as React39 from "react";
6192
6247
  import { Pressable as Pressable12, View as View38 } from "react-native";
6193
6248
  import Animated11, { Easing as Easing2, useAnimatedStyle as useAnimatedStyle2, useSharedValue as useSharedValue2, withTiming as withTiming2 } from "react-native-reanimated";
6194
6249
  import { jsx as jsx50 } from "react/jsx-runtime";
6195
6250
  function ScrollToBottomButton({ visible, onPress, children, style }) {
6196
6251
  const theme = useTheme();
6197
6252
  const progress = useSharedValue2(visible ? 1 : 0);
6198
- const [pressed, setPressed] = React38.useState(false);
6199
- React38.useEffect(() => {
6253
+ const [pressed, setPressed] = React39.useState(false);
6254
+ React39.useEffect(() => {
6200
6255
  progress.value = withTiming2(visible ? 1 : 0, { duration: 200, easing: Easing2.out(Easing2.ease) });
6201
6256
  }, [progress, visible]);
6202
6257
  const animStyle = useAnimatedStyle2(() => ({
@@ -6331,16 +6386,16 @@ function ForkNoticeBanner({ isOwner = true, title, description, style }) {
6331
6386
  }
6332
6387
 
6333
6388
  // src/components/chat/ChatQueue.tsx
6334
- import * as React39 from "react";
6389
+ import * as React40 from "react";
6335
6390
  import { ActivityIndicator as ActivityIndicator8, Pressable as Pressable13, View as View41 } from "react-native";
6336
6391
  import { jsx as jsx53, jsxs as jsxs31 } from "react/jsx-runtime";
6337
6392
  function ChatQueue({ items, onRemove }) {
6338
6393
  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) => {
6394
+ const [expanded, setExpanded] = React40.useState({});
6395
+ const [canExpand, setCanExpand] = React40.useState({});
6396
+ const [collapsedText, setCollapsedText] = React40.useState({});
6397
+ const [removing, setRemoving] = React40.useState({});
6398
+ const buildCollapsedText = React40.useCallback((lines) => {
6344
6399
  var _a, _b;
6345
6400
  const line1 = ((_a = lines[0]) == null ? void 0 : _a.text) ?? "";
6346
6401
  const line2 = ((_b = lines[1]) == null ? void 0 : _b.text) ?? "";
@@ -6356,7 +6411,7 @@ function ChatQueue({ items, onRemove }) {
6356
6411
  return `${line1}
6357
6412
  ${trimmedLine2}\u2026 `;
6358
6413
  }, []);
6359
- React39.useEffect(() => {
6414
+ React40.useEffect(() => {
6360
6415
  if (items.length === 0) return;
6361
6416
  const ids = new Set(items.map((item) => item.id));
6362
6417
  setExpanded((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => ids.has(id))));
@@ -6499,9 +6554,9 @@ function ChatPanel({
6499
6554
  queueItems = [],
6500
6555
  onRemoveQueueItem
6501
6556
  }) {
6502
- const listRef = React40.useRef(null);
6503
- const [nearBottom, setNearBottom] = React40.useState(true);
6504
- const handleSend = React40.useCallback(
6557
+ const listRef = React41.useRef(null);
6558
+ const [nearBottom, setNearBottom] = React41.useState(true);
6559
+ const handleSend = React41.useCallback(
6505
6560
  async (text, composerAttachments) => {
6506
6561
  const all = composerAttachments ?? attachments;
6507
6562
  await onSend(text, all.length > 0 ? all : void 0);
@@ -6515,7 +6570,7 @@ function ChatPanel({
6515
6570
  },
6516
6571
  [attachments, nearBottom, onClearAttachments, onSend]
6517
6572
  );
6518
- const handleScrollToBottom = React40.useCallback(() => {
6573
+ const handleScrollToBottom = React41.useCallback(() => {
6519
6574
  var _a;
6520
6575
  (_a = listRef.current) == null ? void 0 : _a.scrollToBottom({ animated: true });
6521
6576
  }, []);
@@ -6591,7 +6646,7 @@ function ChatPanel({
6591
6646
  }
6592
6647
 
6593
6648
  // src/components/dialogs/ConfirmMergeRequestDialog.tsx
6594
- import * as React41 from "react";
6649
+ import * as React42 from "react";
6595
6650
  import { Pressable as Pressable15, View as View44 } from "react-native";
6596
6651
 
6597
6652
  // src/components/primitives/Modal.tsx
@@ -6643,14 +6698,14 @@ function ConfirmMergeRequestDialog({
6643
6698
  onTestFirst
6644
6699
  }) {
6645
6700
  const theme = useTheme();
6646
- const close = React41.useCallback(() => onOpenChange(false), [onOpenChange]);
6701
+ const close = React42.useCallback(() => onOpenChange(false), [onOpenChange]);
6647
6702
  const canConfirm = Boolean(mergeRequest) && !approveDisabled;
6648
- const handleConfirm = React41.useCallback(() => {
6703
+ const handleConfirm = React42.useCallback(() => {
6649
6704
  if (!mergeRequest) return;
6650
6705
  onOpenChange(false);
6651
6706
  void onConfirm();
6652
6707
  }, [mergeRequest, onConfirm, onOpenChange]);
6653
- const handleTestFirst = React41.useCallback(() => {
6708
+ const handleTestFirst = React42.useCallback(() => {
6654
6709
  if (!mergeRequest) return;
6655
6710
  onOpenChange(false);
6656
6711
  void onTestFirst(mergeRequest);
@@ -6799,7 +6854,7 @@ function ConfirmMergeFlow({
6799
6854
  }
6800
6855
 
6801
6856
  // src/studio/hooks/useOptimisticChatMessages.ts
6802
- import * as React42 from "react";
6857
+ import * as React43 from "react";
6803
6858
  function makeOptimisticId() {
6804
6859
  return `optimistic:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 10)}`;
6805
6860
  }
@@ -6838,11 +6893,11 @@ function useOptimisticChatMessages({
6838
6893
  chatMessages,
6839
6894
  onSendChat
6840
6895
  }) {
6841
- const [optimisticChat, setOptimisticChat] = React42.useState([]);
6842
- React42.useEffect(() => {
6896
+ const [optimisticChat, setOptimisticChat] = React43.useState([]);
6897
+ React43.useEffect(() => {
6843
6898
  setOptimisticChat([]);
6844
6899
  }, [threadId]);
6845
- const messages = React42.useMemo(() => {
6900
+ const messages = React43.useMemo(() => {
6846
6901
  if (!optimisticChat || optimisticChat.length === 0) return chatMessages;
6847
6902
  const unresolved = optimisticChat.filter((o) => !isOptimisticResolvedByServer(chatMessages, o));
6848
6903
  if (unresolved.length === 0) return chatMessages;
@@ -6858,7 +6913,7 @@ function useOptimisticChatMessages({
6858
6913
  merged.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
6859
6914
  return merged;
6860
6915
  }, [chatMessages, optimisticChat]);
6861
- React42.useEffect(() => {
6916
+ React43.useEffect(() => {
6862
6917
  if (optimisticChat.length === 0) return;
6863
6918
  setOptimisticChat((prev) => {
6864
6919
  if (prev.length === 0) return prev;
@@ -6866,7 +6921,7 @@ function useOptimisticChatMessages({
6866
6921
  return next.length === prev.length ? prev : next;
6867
6922
  });
6868
6923
  }, [chatMessages, optimisticChat.length]);
6869
- const onSend = React42.useCallback(
6924
+ const onSend = React43.useCallback(
6870
6925
  async (text, attachments) => {
6871
6926
  if (shouldForkOnEdit || disableOptimistic) {
6872
6927
  await onSendChat(text, attachments);
@@ -6925,15 +6980,17 @@ function StudioOverlay({
6925
6980
  }) {
6926
6981
  const theme = useTheme();
6927
6982
  const { width } = useWindowDimensions4();
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);
6983
+ const [sheetOpen, setSheetOpen] = React44.useState(false);
6984
+ const sheetOpenRef = React44.useRef(sheetOpen);
6985
+ const [activePage, setActivePage] = React44.useState("preview");
6986
+ const [drawing, setDrawing] = React44.useState(false);
6987
+ const [chatAttachments, setChatAttachments] = React44.useState([]);
6988
+ const [commentsAppId, setCommentsAppId] = React44.useState(null);
6989
+ const [commentsCount, setCommentsCount] = React44.useState(null);
6935
6990
  const threadId = (app == null ? void 0 : app.threadId) ?? null;
6936
- const disableOptimistic = Boolean(chatQueueItems && chatQueueItems.length > 0) || (app == null ? void 0 : app.status) === "editing";
6991
+ const isForking = chatForking || (app == null ? void 0 : app.status) === "forking";
6992
+ const queueItemsForChat = isForking ? [] : chatQueueItems;
6993
+ const disableOptimistic = Boolean(queueItemsForChat && queueItemsForChat.length > 0) || (app == null ? void 0 : app.status) === "editing";
6937
6994
  const optimistic = useOptimisticChatMessages({
6938
6995
  threadId,
6939
6996
  shouldForkOnEdit,
@@ -6941,25 +6998,25 @@ function StudioOverlay({
6941
6998
  chatMessages,
6942
6999
  onSendChat
6943
7000
  });
6944
- const [confirmMrId, setConfirmMrId] = React43.useState(null);
6945
- const confirmMr = React43.useMemo(
7001
+ const [confirmMrId, setConfirmMrId] = React44.useState(null);
7002
+ const confirmMr = React44.useMemo(
6946
7003
  () => confirmMrId ? incomingMergeRequests.find((m) => m.id === confirmMrId) ?? null : null,
6947
7004
  [confirmMrId, incomingMergeRequests]
6948
7005
  );
6949
- const handleSheetOpenChange = React43.useCallback((open) => {
7006
+ const handleSheetOpenChange = React44.useCallback((open) => {
6950
7007
  setSheetOpen(open);
6951
7008
  if (!open) Keyboard5.dismiss();
6952
7009
  }, []);
6953
- const closeSheet = React43.useCallback(() => {
7010
+ const closeSheet = React44.useCallback(() => {
6954
7011
  handleSheetOpenChange(false);
6955
7012
  }, [handleSheetOpenChange]);
6956
- const openSheet = React43.useCallback(() => setSheetOpen(true), []);
6957
- const goToChat = React43.useCallback(() => {
7013
+ const openSheet = React44.useCallback(() => setSheetOpen(true), []);
7014
+ const goToChat = React44.useCallback(() => {
6958
7015
  setActivePage("chat");
6959
7016
  openSheet();
6960
7017
  }, [openSheet]);
6961
- const backToPreview = React43.useCallback(() => {
6962
- if (Platform10.OS !== "ios") {
7018
+ const backToPreview = React44.useCallback(() => {
7019
+ if (Platform11.OS !== "ios") {
6963
7020
  Keyboard5.dismiss();
6964
7021
  setActivePage("preview");
6965
7022
  return;
@@ -6976,11 +7033,11 @@ function StudioOverlay({
6976
7033
  const t = setTimeout(finalize, 350);
6977
7034
  Keyboard5.dismiss();
6978
7035
  }, []);
6979
- const startDraw = React43.useCallback(() => {
7036
+ const startDraw = React44.useCallback(() => {
6980
7037
  setDrawing(true);
6981
7038
  closeSheet();
6982
7039
  }, [closeSheet]);
6983
- const handleDrawCapture = React43.useCallback(
7040
+ const handleDrawCapture = React44.useCallback(
6984
7041
  (dataUrl) => {
6985
7042
  setChatAttachments((prev) => [...prev, dataUrl]);
6986
7043
  setDrawing(false);
@@ -6989,7 +7046,7 @@ function StudioOverlay({
6989
7046
  },
6990
7047
  [openSheet]
6991
7048
  );
6992
- const toggleSheet = React43.useCallback(async () => {
7049
+ const toggleSheet = React44.useCallback(async () => {
6993
7050
  if (!sheetOpen) {
6994
7051
  const shouldExitTest = Boolean(testingMrId) || isTesting;
6995
7052
  if (shouldExitTest) {
@@ -7001,7 +7058,7 @@ function StudioOverlay({
7001
7058
  closeSheet();
7002
7059
  }
7003
7060
  }, [closeSheet, isTesting, onRestoreBase, sheetOpen, testingMrId]);
7004
- const handleTestMr = React43.useCallback(
7061
+ const handleTestMr = React44.useCallback(
7005
7062
  async (mr) => {
7006
7063
  if (!onTestMr) return;
7007
7064
  await onTestMr(mr);
@@ -7009,10 +7066,10 @@ function StudioOverlay({
7009
7066
  },
7010
7067
  [closeSheet, onTestMr]
7011
7068
  );
7012
- React43.useEffect(() => {
7069
+ React44.useEffect(() => {
7013
7070
  sheetOpenRef.current = sheetOpen;
7014
7071
  }, [sheetOpen]);
7015
- React43.useEffect(() => {
7072
+ React44.useEffect(() => {
7016
7073
  const poller = startStudioControlPolling((action) => {
7017
7074
  if (action === "show" && !sheetOpenRef.current) openSheet();
7018
7075
  if (action === "hide" && sheetOpenRef.current) closeSheet();
@@ -7020,7 +7077,7 @@ function StudioOverlay({
7020
7077
  }, studioControlOptions);
7021
7078
  return () => poller.stop();
7022
7079
  }, [closeSheet, openSheet, studioControlOptions, toggleSheet]);
7023
- React43.useEffect(() => {
7080
+ React44.useEffect(() => {
7024
7081
  void publishComergeStudioUIState(sheetOpen, studioControlOptions);
7025
7082
  }, [sheetOpen, studioControlOptions]);
7026
7083
  return /* @__PURE__ */ jsxs35(Fragment6, { children: [
@@ -7075,7 +7132,7 @@ function StudioOverlay({
7075
7132
  onNavigateHome,
7076
7133
  onStartDraw: startDraw,
7077
7134
  onSend: optimistic.onSend,
7078
- queueItems: chatQueueItems,
7135
+ queueItems: queueItemsForChat,
7079
7136
  onRemoveQueueItem
7080
7137
  }
7081
7138
  )
@@ -7127,7 +7184,7 @@ function StudioOverlay({
7127
7184
  }
7128
7185
 
7129
7186
  // src/studio/hooks/useEditQueue.ts
7130
- import * as React44 from "react";
7187
+ import * as React45 from "react";
7131
7188
 
7132
7189
  // src/data/apps/edit-queue/remote.ts
7133
7190
  var EditQueueRemoteDataSourceImpl = class extends BaseRemote {
@@ -7238,17 +7295,17 @@ var editQueueRepository = new EditQueueRepositoryImpl(
7238
7295
 
7239
7296
  // src/studio/hooks/useEditQueue.ts
7240
7297
  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);
7298
+ const [items, setItems] = React45.useState([]);
7299
+ const [loading, setLoading] = React45.useState(false);
7300
+ const [error, setError] = React45.useState(null);
7301
+ const activeRequestIdRef = React45.useRef(0);
7245
7302
  const foregroundSignal = useForegroundSignal(Boolean(appId));
7246
- const upsertSorted = React44.useCallback((prev, nextItem) => {
7303
+ const upsertSorted = React45.useCallback((prev, nextItem) => {
7247
7304
  const next = prev.some((x) => x.id === nextItem.id) ? prev.map((x) => x.id === nextItem.id ? nextItem : x) : [...prev, nextItem];
7248
7305
  next.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
7249
7306
  return next;
7250
7307
  }, []);
7251
- const refetch = React44.useCallback(async () => {
7308
+ const refetch = React45.useCallback(async () => {
7252
7309
  if (!appId) {
7253
7310
  setItems([]);
7254
7311
  return;
@@ -7268,10 +7325,10 @@ function useEditQueue(appId) {
7268
7325
  if (activeRequestIdRef.current === requestId) setLoading(false);
7269
7326
  }
7270
7327
  }, [appId]);
7271
- React44.useEffect(() => {
7328
+ React45.useEffect(() => {
7272
7329
  void refetch();
7273
7330
  }, [refetch]);
7274
- React44.useEffect(() => {
7331
+ React45.useEffect(() => {
7275
7332
  if (!appId) return;
7276
7333
  const unsubscribe = editQueueRepository.subscribeEditQueue(appId, {
7277
7334
  onInsert: (item) => setItems((prev) => upsertSorted(prev, item)),
@@ -7280,7 +7337,7 @@ function useEditQueue(appId) {
7280
7337
  });
7281
7338
  return unsubscribe;
7282
7339
  }, [appId, upsertSorted, foregroundSignal]);
7283
- React44.useEffect(() => {
7340
+ React45.useEffect(() => {
7284
7341
  if (!appId) return;
7285
7342
  if (foregroundSignal <= 0) return;
7286
7343
  void refetch();
@@ -7289,16 +7346,16 @@ function useEditQueue(appId) {
7289
7346
  }
7290
7347
 
7291
7348
  // src/studio/hooks/useEditQueueActions.ts
7292
- import * as React45 from "react";
7349
+ import * as React46 from "react";
7293
7350
  function useEditQueueActions(appId) {
7294
- const update = React45.useCallback(
7351
+ const update = React46.useCallback(
7295
7352
  async (queueItemId, payload) => {
7296
7353
  if (!appId) return;
7297
7354
  await editQueueRepository.update(appId, queueItemId, payload);
7298
7355
  },
7299
7356
  [appId]
7300
7357
  );
7301
- const cancel = React45.useCallback(
7358
+ const cancel = React46.useCallback(
7302
7359
  async (queueItemId) => {
7303
7360
  if (!appId) return;
7304
7361
  await editQueueRepository.cancel(appId, queueItemId);
@@ -7320,16 +7377,16 @@ function ComergeStudio({
7320
7377
  studioControlOptions,
7321
7378
  embeddedBaseBundles
7322
7379
  }) {
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(() => {
7380
+ const [activeAppId, setActiveAppId] = React47.useState(appId);
7381
+ const [runtimeAppId, setRuntimeAppId] = React47.useState(appId);
7382
+ const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React47.useState(null);
7383
+ const platform = React47.useMemo(() => RNPlatform.OS === "ios" ? "ios" : "android", []);
7384
+ React47.useEffect(() => {
7328
7385
  setActiveAppId(appId);
7329
7386
  setRuntimeAppId(appId);
7330
7387
  setPendingRuntimeTargetAppId(null);
7331
7388
  }, [appId]);
7332
- const captureTargetRef = React46.useRef(null);
7389
+ const captureTargetRef = React47.useRef(null);
7333
7390
  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(
7334
7391
  ComergeStudioInner,
7335
7392
  {
@@ -7371,11 +7428,11 @@ function ComergeStudioInner({
7371
7428
  const { app, loading: appLoading } = useApp(activeAppId);
7372
7429
  const { app: runtimeAppFromHook } = useApp(runtimeAppId, { enabled: runtimeAppId !== activeAppId });
7373
7430
  const runtimeApp = runtimeAppId === activeAppId ? app : runtimeAppFromHook;
7374
- const sawEditingOnPendingTargetRef = React46.useRef(false);
7375
- React46.useEffect(() => {
7431
+ const sawEditingOnPendingTargetRef = React47.useRef(false);
7432
+ React47.useEffect(() => {
7376
7433
  sawEditingOnPendingTargetRef.current = false;
7377
7434
  }, [pendingRuntimeTargetAppId]);
7378
- React46.useEffect(() => {
7435
+ React47.useEffect(() => {
7379
7436
  if (!pendingRuntimeTargetAppId) return;
7380
7437
  if (activeAppId !== pendingRuntimeTargetAppId) return;
7381
7438
  if ((app == null ? void 0 : app.status) === "editing") {
@@ -7393,13 +7450,13 @@ function ComergeStudioInner({
7393
7450
  canRequestLatest: (runtimeApp == null ? void 0 : runtimeApp.status) === "ready",
7394
7451
  embeddedBaseBundles
7395
7452
  });
7396
- const sawEditingOnActiveAppRef = React46.useRef(false);
7397
- const [showPostEditPreparing, setShowPostEditPreparing] = React46.useState(false);
7398
- React46.useEffect(() => {
7453
+ const sawEditingOnActiveAppRef = React47.useRef(false);
7454
+ const [showPostEditPreparing, setShowPostEditPreparing] = React47.useState(false);
7455
+ React47.useEffect(() => {
7399
7456
  sawEditingOnActiveAppRef.current = false;
7400
7457
  setShowPostEditPreparing(false);
7401
7458
  }, [activeAppId]);
7402
- React46.useEffect(() => {
7459
+ React47.useEffect(() => {
7403
7460
  if (!(app == null ? void 0 : app.id)) return;
7404
7461
  if (app.status === "editing") {
7405
7462
  sawEditingOnActiveAppRef.current = true;
@@ -7411,7 +7468,7 @@ function ComergeStudioInner({
7411
7468
  sawEditingOnActiveAppRef.current = false;
7412
7469
  }
7413
7470
  }, [app == null ? void 0 : app.id, app == null ? void 0 : app.status]);
7414
- React46.useEffect(() => {
7471
+ React47.useEffect(() => {
7415
7472
  if (!showPostEditPreparing) return;
7416
7473
  const stillProcessingBaseBundle = bundle.loading && bundle.loadingMode === "base" && !bundle.isTesting;
7417
7474
  if (!stillProcessingBaseBundle) {
@@ -7422,19 +7479,19 @@ function ComergeStudioInner({
7422
7479
  const thread = useThreadMessages(threadId);
7423
7480
  const editQueue = useEditQueue(activeAppId);
7424
7481
  const editQueueActions = useEditQueueActions(activeAppId);
7425
- const [lastEditQueueInfo, setLastEditQueueInfo] = React46.useState(null);
7426
- const lastEditQueueInfoRef = React46.useRef(null);
7427
- const [suppressQueueUntilResponse, setSuppressQueueUntilResponse] = React46.useState(false);
7482
+ const [lastEditQueueInfo, setLastEditQueueInfo] = React47.useState(null);
7483
+ const lastEditQueueInfoRef = React47.useRef(null);
7484
+ const [suppressQueueUntilResponse, setSuppressQueueUntilResponse] = React47.useState(false);
7428
7485
  const mergeRequests = useMergeRequests({ appId: activeAppId });
7429
- const hasOpenOutgoingMr = React46.useMemo(() => {
7486
+ const hasOpenOutgoingMr = React47.useMemo(() => {
7430
7487
  return mergeRequests.lists.outgoing.some((mr) => mr.status === "open");
7431
7488
  }, [mergeRequests.lists.outgoing]);
7432
- const incomingReviewMrs = React46.useMemo(() => {
7489
+ const incomingReviewMrs = React47.useMemo(() => {
7433
7490
  if (!userId) return mergeRequests.lists.incoming;
7434
7491
  return mergeRequests.lists.incoming.filter((mr) => mr.createdBy !== userId);
7435
7492
  }, [mergeRequests.lists.incoming, userId]);
7436
7493
  const uploader = useAttachmentUpload();
7437
- const updateLastEditQueueInfo = React46.useCallback(
7494
+ const updateLastEditQueueInfo = React47.useCallback(
7438
7495
  (info) => {
7439
7496
  lastEditQueueInfoRef.current = info;
7440
7497
  setLastEditQueueInfo(info);
@@ -7475,20 +7532,20 @@ function ComergeStudioInner({
7475
7532
  }
7476
7533
  });
7477
7534
  const chatSendDisabled = false;
7478
- const [processingMrId, setProcessingMrId] = React46.useState(null);
7479
- const [testingMrId, setTestingMrId] = React46.useState(null);
7480
- const chatShowTypingIndicator = React46.useMemo(() => {
7535
+ const [processingMrId, setProcessingMrId] = React47.useState(null);
7536
+ const [testingMrId, setTestingMrId] = React47.useState(null);
7537
+ const chatShowTypingIndicator = React47.useMemo(() => {
7481
7538
  var _a;
7482
7539
  if (!thread.raw || thread.raw.length === 0) return false;
7483
7540
  const last = thread.raw[thread.raw.length - 1];
7484
7541
  const payloadType = typeof ((_a = last.payload) == null ? void 0 : _a.type) === "string" ? String(last.payload.type) : void 0;
7485
7542
  return payloadType !== "outcome";
7486
7543
  }, [thread.raw]);
7487
- React46.useEffect(() => {
7544
+ React47.useEffect(() => {
7488
7545
  updateLastEditQueueInfo(null);
7489
7546
  setSuppressQueueUntilResponse(false);
7490
7547
  }, [activeAppId, updateLastEditQueueInfo]);
7491
- React46.useEffect(() => {
7548
+ React47.useEffect(() => {
7492
7549
  if (!(lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId)) return;
7493
7550
  const stillPresent = editQueue.items.some((item) => item.id === lastEditQueueInfo.queueItemId);
7494
7551
  if (!stillPresent) {
@@ -7496,7 +7553,7 @@ function ComergeStudioInner({
7496
7553
  setSuppressQueueUntilResponse(false);
7497
7554
  }
7498
7555
  }, [editQueue.items, lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId]);
7499
- const chatQueueItems = React46.useMemo(() => {
7556
+ const chatQueueItems = React47.useMemo(() => {
7500
7557
  var _a;
7501
7558
  if (suppressQueueUntilResponse && editQueue.items.length <= 1) {
7502
7559
  return [];