@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.js CHANGED
@@ -36,7 +36,7 @@ __export(index_exports, {
36
36
  module.exports = __toCommonJS(index_exports);
37
37
 
38
38
  // src/studio/ComergeStudio.tsx
39
- var React46 = __toESM(require("react"));
39
+ var React47 = __toESM(require("react"));
40
40
  var import_react_native56 = require("react-native");
41
41
  var import_bottom_sheet6 = require("@gorhom/bottom-sheet");
42
42
 
@@ -894,9 +894,8 @@ function useThreadMessages(threadId) {
894
894
  const activeRequestIdRef = React4.useRef(0);
895
895
  const foregroundSignal = useForegroundSignal(Boolean(threadId));
896
896
  const upsertSorted = React4.useCallback((prev, m) => {
897
- const include = !isQueuedHiddenMessage(m);
898
897
  const next = prev.filter((x) => x.id !== m.id);
899
- if (include) next.push(m);
898
+ next.push(m);
900
899
  next.sort(compareMessages);
901
900
  return next;
902
901
  }, []);
@@ -911,7 +910,7 @@ function useThreadMessages(threadId) {
911
910
  try {
912
911
  const list = await messagesRepository.list(threadId);
913
912
  if (activeRequestIdRef.current !== requestId) return;
914
- setRaw([...list].filter((m) => !isQueuedHiddenMessage(m)).sort(compareMessages));
913
+ setRaw([...list].sort(compareMessages));
915
914
  } catch (e) {
916
915
  if (activeRequestIdRef.current !== requestId) return;
917
916
  setError(e instanceof Error ? e : new Error(String(e)));
@@ -937,7 +936,11 @@ function useThreadMessages(threadId) {
937
936
  if (foregroundSignal <= 0) return;
938
937
  void refetch();
939
938
  }, [foregroundSignal, refetch, threadId]);
940
- const messages = React4.useMemo(() => raw.map(mapMessageToChatMessage), [raw]);
939
+ const messages = React4.useMemo(() => {
940
+ const visible = raw.filter((m) => !isQueuedHiddenMessage(m));
941
+ const resolved = visible.length > 0 ? visible : raw;
942
+ return resolved.map(mapMessageToChatMessage);
943
+ }, [raw]);
941
944
  return { raw, messages, loading, error, refetch };
942
945
  }
943
946
 
@@ -1981,7 +1984,6 @@ function useStudioActions({
1981
1984
  setSending(true);
1982
1985
  setError(null);
1983
1986
  try {
1984
- onEditStart == null ? void 0 : onEditStart();
1985
1987
  let targetApp = app;
1986
1988
  if (shouldForkOnEdit) {
1987
1989
  setForking(true);
@@ -1993,6 +1995,7 @@ function useStudioActions({
1993
1995
  setForking(false);
1994
1996
  const threadId = targetApp.threadId;
1995
1997
  if (!threadId) throw new Error("No thread available for this app.");
1998
+ onEditStart == null ? void 0 : onEditStart();
1996
1999
  let attachmentMetas;
1997
2000
  if (attachments && attachments.length > 0 && uploadAttachments) {
1998
2001
  attachmentMetas = await uploadAttachments({ threadId, appId: targetApp.id, dataUrls: attachments });
@@ -2059,7 +2062,7 @@ function RuntimeRenderer({
2059
2062
  }
2060
2063
 
2061
2064
  // src/studio/ui/StudioOverlay.tsx
2062
- var React43 = __toESM(require("react"));
2065
+ var React44 = __toESM(require("react"));
2063
2066
  var import_react_native55 = require("react-native");
2064
2067
 
2065
2068
  // src/components/studio-sheet/StudioBottomSheet.tsx
@@ -3249,6 +3252,7 @@ var IconSend = makeIcon(import_lucide_react_native2.Send);
3249
3252
  var IconPlay = makeIcon(import_lucide_react_native2.Play);
3250
3253
  var IconArrowDown = makeIcon(import_lucide_react_native2.ArrowDown);
3251
3254
  var IconApprove = makeIcon(import_lucide_react_native2.Check);
3255
+ var IconShare = makeIcon(import_lucide_react_native2.Share2);
3252
3256
 
3253
3257
  // src/components/chat/ChatComposer.tsx
3254
3258
  var import_jsx_runtime17 = require("react/jsx-runtime");
@@ -3965,6 +3969,7 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3965
3969
  }
3966
3970
 
3967
3971
  // src/studio/ui/PreviewPanel.tsx
3972
+ var React35 = __toESM(require("react"));
3968
3973
  var import_react_native43 = require("react-native");
3969
3974
 
3970
3975
  // src/components/preview/PreviewPage.tsx
@@ -4115,7 +4120,14 @@ function StudioSheetHeaderIconButton({
4115
4120
 
4116
4121
  // src/studio/ui/preview-panel/PreviewPanelHeader.tsx
4117
4122
  var import_jsx_runtime24 = require("react/jsx-runtime");
4118
- function PreviewPanelHeader({ isOwner, onClose, onNavigateHome, onGoToChat }) {
4123
+ function PreviewPanelHeader({
4124
+ isOwner,
4125
+ isPublic,
4126
+ onClose,
4127
+ onNavigateHome,
4128
+ onGoToChat,
4129
+ onShare
4130
+ }) {
4119
4131
  return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4120
4132
  StudioSheetHeader,
4121
4133
  {
@@ -4133,6 +4145,17 @@ function PreviewPanelHeader({ isOwner, onClose, onNavigateHome, onGoToChat }) {
4133
4145
  children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(IconChat, { size: 20, colorToken: "onPrimary" })
4134
4146
  }
4135
4147
  ) : null,
4148
+ isPublic && onShare ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
4149
+ StudioSheetHeaderIconButton,
4150
+ {
4151
+ onPress: onShare,
4152
+ accessibilityLabel: "Share",
4153
+ intent: "primary",
4154
+ appearance: "glass",
4155
+ style: { marginRight: 8 },
4156
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(IconShare, { size: 20, colorToken: "onPrimary" })
4157
+ }
4158
+ ) : null,
4136
4159
  /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", appearance: "glass", intent: "primary", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(IconClose, { size: 20, colorToken: "onPrimary" }) })
4137
4160
  ] })
4138
4161
  }
@@ -5797,6 +5820,27 @@ function PreviewPanel({
5797
5820
  onOpenComments,
5798
5821
  commentCountOverride
5799
5822
  }) {
5823
+ const handleShare = React35.useCallback(async () => {
5824
+ if (!app || !app.isPublic) return;
5825
+ const shareUrl = `https://comerge.ai/app/${app.id}`;
5826
+ const message = app.name ? `${app.name} on Comerge
5827
+ ${shareUrl}` : `Check out this app on Comerge
5828
+ ${shareUrl}`;
5829
+ try {
5830
+ const title = app.name ?? "Comerge app";
5831
+ const payload = import_react_native43.Platform.OS === "ios" ? {
5832
+ title,
5833
+ message
5834
+ } : {
5835
+ title,
5836
+ message,
5837
+ url: shareUrl
5838
+ };
5839
+ await import_react_native43.Share.share(payload);
5840
+ } catch (error) {
5841
+ log.warn("PreviewPanel share failed", error);
5842
+ }
5843
+ }, [app]);
5800
5844
  const { imageUrl, imageLoaded, setImageLoaded, creator, insights, stats, showProcessing, canSubmitMergeRequest } = usePreviewPanelData({
5801
5845
  app,
5802
5846
  isOwner,
@@ -5804,7 +5848,17 @@ function PreviewPanel({
5804
5848
  onOpenComments,
5805
5849
  commentCountOverride
5806
5850
  });
5807
- const header = /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(PreviewPanelHeader, { isOwner, onClose, onNavigateHome, onGoToChat });
5851
+ const header = /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
5852
+ PreviewPanelHeader,
5853
+ {
5854
+ isOwner,
5855
+ isPublic: Boolean(app == null ? void 0 : app.isPublic),
5856
+ onClose,
5857
+ onNavigateHome,
5858
+ onGoToChat,
5859
+ onShare: handleShare
5860
+ }
5861
+ );
5808
5862
  if (loading || !app) {
5809
5863
  return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(PreviewPage, { header, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_react_native43.View, { style: { flex: 1, justifyContent: "center", alignItems: "center", padding: 24 }, children: [
5810
5864
  /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_react_native43.ActivityIndicator, {}),
@@ -5864,16 +5918,16 @@ function PreviewPanel({
5864
5918
  }
5865
5919
 
5866
5920
  // src/studio/ui/ChatPanel.tsx
5867
- var React40 = __toESM(require("react"));
5921
+ var React41 = __toESM(require("react"));
5868
5922
  var import_react_native52 = require("react-native");
5869
5923
 
5870
5924
  // src/components/chat/ChatPage.tsx
5871
- var React37 = __toESM(require("react"));
5925
+ var React38 = __toESM(require("react"));
5872
5926
  var import_react_native47 = require("react-native");
5873
5927
  var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
5874
5928
 
5875
5929
  // src/components/chat/ChatMessageList.tsx
5876
- var React36 = __toESM(require("react"));
5930
+ var React37 = __toESM(require("react"));
5877
5931
  var import_react_native46 = require("react-native");
5878
5932
  var import_bottom_sheet5 = require("@gorhom/bottom-sheet");
5879
5933
 
@@ -5919,17 +5973,17 @@ function ChatMessageBubble({ message, renderContent, style }) {
5919
5973
  }
5920
5974
 
5921
5975
  // src/components/chat/TypingIndicator.tsx
5922
- var React35 = __toESM(require("react"));
5976
+ var React36 = __toESM(require("react"));
5923
5977
  var import_react_native45 = require("react-native");
5924
5978
  var import_jsx_runtime47 = require("react/jsx-runtime");
5925
5979
  function TypingIndicator({ style }) {
5926
5980
  const theme = useTheme();
5927
5981
  const dotColor = theme.colors.textSubtle;
5928
- const anims = React35.useMemo(
5982
+ const anims = React36.useMemo(
5929
5983
  () => [new import_react_native45.Animated.Value(0.3), new import_react_native45.Animated.Value(0.3), new import_react_native45.Animated.Value(0.3)],
5930
5984
  []
5931
5985
  );
5932
- React35.useEffect(() => {
5986
+ React36.useEffect(() => {
5933
5987
  const loops = [];
5934
5988
  anims.forEach((a, idx) => {
5935
5989
  const seq = import_react_native45.Animated.sequence([
@@ -5963,7 +6017,7 @@ function TypingIndicator({ style }) {
5963
6017
 
5964
6018
  // src/components/chat/ChatMessageList.tsx
5965
6019
  var import_jsx_runtime48 = require("react/jsx-runtime");
5966
- var ChatMessageList = React36.forwardRef(
6020
+ var ChatMessageList = React37.forwardRef(
5967
6021
  ({
5968
6022
  messages,
5969
6023
  showTypingIndicator = false,
@@ -5974,20 +6028,20 @@ var ChatMessageList = React36.forwardRef(
5974
6028
  nearBottomThreshold = 200
5975
6029
  }, ref) => {
5976
6030
  const theme = useTheme();
5977
- const listRef = React36.useRef(null);
5978
- const nearBottomRef = React36.useRef(true);
5979
- const initialScrollDoneRef = React36.useRef(false);
5980
- const lastMessageIdRef = React36.useRef(null);
5981
- const data = React36.useMemo(() => {
6031
+ const listRef = React37.useRef(null);
6032
+ const nearBottomRef = React37.useRef(true);
6033
+ const initialScrollDoneRef = React37.useRef(false);
6034
+ const lastMessageIdRef = React37.useRef(null);
6035
+ const data = React37.useMemo(() => {
5982
6036
  return [...messages].reverse();
5983
6037
  }, [messages]);
5984
- const scrollToBottom = React36.useCallback((options) => {
6038
+ const scrollToBottom = React37.useCallback((options) => {
5985
6039
  var _a;
5986
6040
  const animated = (options == null ? void 0 : options.animated) ?? true;
5987
6041
  (_a = listRef.current) == null ? void 0 : _a.scrollToOffset({ offset: 0, animated });
5988
6042
  }, []);
5989
- React36.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
5990
- const handleScroll = React36.useCallback(
6043
+ React37.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
6044
+ const handleScroll = React37.useCallback(
5991
6045
  (e) => {
5992
6046
  const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;
5993
6047
  const distanceFromBottom = Math.max(contentOffset.y - Math.max(bottomInset, 0), 0);
@@ -5999,7 +6053,7 @@ var ChatMessageList = React36.forwardRef(
5999
6053
  },
6000
6054
  [bottomInset, nearBottomThreshold, onNearBottomChange]
6001
6055
  );
6002
- React36.useEffect(() => {
6056
+ React37.useEffect(() => {
6003
6057
  if (!initialScrollDoneRef.current) return;
6004
6058
  const lastId = messages.length > 0 ? messages[messages.length - 1].id : null;
6005
6059
  const prevLastId = lastMessageIdRef.current;
@@ -6009,7 +6063,7 @@ var ChatMessageList = React36.forwardRef(
6009
6063
  const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
6010
6064
  return () => cancelAnimationFrame(id);
6011
6065
  }, [messages, scrollToBottom]);
6012
- React36.useEffect(() => {
6066
+ React37.useEffect(() => {
6013
6067
  if (showTypingIndicator && nearBottomRef.current) {
6014
6068
  const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
6015
6069
  return () => cancelAnimationFrame(id);
@@ -6072,10 +6126,10 @@ function ChatPage({
6072
6126
  }) {
6073
6127
  const theme = useTheme();
6074
6128
  const insets = (0, import_react_native_safe_area_context4.useSafeAreaInsets)();
6075
- const [composerHeight, setComposerHeight] = React37.useState(0);
6076
- const [composerTopHeight, setComposerTopHeight] = React37.useState(0);
6077
- const [keyboardVisible, setKeyboardVisible] = React37.useState(false);
6078
- React37.useEffect(() => {
6129
+ const [composerHeight, setComposerHeight] = React38.useState(0);
6130
+ const [composerTopHeight, setComposerTopHeight] = React38.useState(0);
6131
+ const [keyboardVisible, setKeyboardVisible] = React38.useState(false);
6132
+ React38.useEffect(() => {
6079
6133
  if (import_react_native47.Platform.OS !== "ios") return;
6080
6134
  const show = import_react_native47.Keyboard.addListener("keyboardWillShow", () => setKeyboardVisible(true));
6081
6135
  const hide = import_react_native47.Keyboard.addListener("keyboardWillHide", () => setKeyboardVisible(false));
@@ -6088,16 +6142,16 @@ function ChatPage({
6088
6142
  const totalComposerHeight = composerHeight + composerTopHeight;
6089
6143
  const overlayBottom = totalComposerHeight + footerBottomPadding + theme.spacing.lg;
6090
6144
  const bottomInset = totalComposerHeight + footerBottomPadding + theme.spacing.xl;
6091
- const resolvedOverlay = React37.useMemo(() => {
6145
+ const resolvedOverlay = React38.useMemo(() => {
6092
6146
  var _a;
6093
6147
  if (!overlay) return null;
6094
- if (!React37.isValidElement(overlay)) return overlay;
6148
+ if (!React38.isValidElement(overlay)) return overlay;
6095
6149
  const prevStyle = (_a = overlay.props) == null ? void 0 : _a.style;
6096
- return React37.cloneElement(overlay, {
6150
+ return React38.cloneElement(overlay, {
6097
6151
  style: [prevStyle, { bottom: overlayBottom }]
6098
6152
  });
6099
6153
  }, [overlay, overlayBottom]);
6100
- React37.useEffect(() => {
6154
+ React38.useEffect(() => {
6101
6155
  if (composerTop) return;
6102
6156
  setComposerTopHeight(0);
6103
6157
  }, [composerTop]);
@@ -6162,15 +6216,15 @@ function ChatPage({
6162
6216
  }
6163
6217
 
6164
6218
  // src/components/chat/ScrollToBottomButton.tsx
6165
- var React38 = __toESM(require("react"));
6219
+ var React39 = __toESM(require("react"));
6166
6220
  var import_react_native48 = require("react-native");
6167
6221
  var import_react_native_reanimated2 = __toESM(require("react-native-reanimated"));
6168
6222
  var import_jsx_runtime50 = require("react/jsx-runtime");
6169
6223
  function ScrollToBottomButton({ visible, onPress, children, style }) {
6170
6224
  const theme = useTheme();
6171
6225
  const progress = (0, import_react_native_reanimated2.useSharedValue)(visible ? 1 : 0);
6172
- const [pressed, setPressed] = React38.useState(false);
6173
- React38.useEffect(() => {
6226
+ const [pressed, setPressed] = React39.useState(false);
6227
+ React39.useEffect(() => {
6174
6228
  progress.value = (0, import_react_native_reanimated2.withTiming)(visible ? 1 : 0, { duration: 200, easing: import_react_native_reanimated2.Easing.out(import_react_native_reanimated2.Easing.ease) });
6175
6229
  }, [progress, visible]);
6176
6230
  const animStyle = (0, import_react_native_reanimated2.useAnimatedStyle)(() => ({
@@ -6305,16 +6359,16 @@ function ForkNoticeBanner({ isOwner = true, title, description, style }) {
6305
6359
  }
6306
6360
 
6307
6361
  // src/components/chat/ChatQueue.tsx
6308
- var React39 = __toESM(require("react"));
6362
+ var React40 = __toESM(require("react"));
6309
6363
  var import_react_native51 = require("react-native");
6310
6364
  var import_jsx_runtime53 = require("react/jsx-runtime");
6311
6365
  function ChatQueue({ items, onRemove }) {
6312
6366
  const theme = useTheme();
6313
- const [expanded, setExpanded] = React39.useState({});
6314
- const [canExpand, setCanExpand] = React39.useState({});
6315
- const [collapsedText, setCollapsedText] = React39.useState({});
6316
- const [removing, setRemoving] = React39.useState({});
6317
- const buildCollapsedText = React39.useCallback((lines) => {
6367
+ const [expanded, setExpanded] = React40.useState({});
6368
+ const [canExpand, setCanExpand] = React40.useState({});
6369
+ const [collapsedText, setCollapsedText] = React40.useState({});
6370
+ const [removing, setRemoving] = React40.useState({});
6371
+ const buildCollapsedText = React40.useCallback((lines) => {
6318
6372
  var _a, _b;
6319
6373
  const line1 = ((_a = lines[0]) == null ? void 0 : _a.text) ?? "";
6320
6374
  const line2 = ((_b = lines[1]) == null ? void 0 : _b.text) ?? "";
@@ -6330,7 +6384,7 @@ function ChatQueue({ items, onRemove }) {
6330
6384
  return `${line1}
6331
6385
  ${trimmedLine2}\u2026 `;
6332
6386
  }, []);
6333
- React39.useEffect(() => {
6387
+ React40.useEffect(() => {
6334
6388
  if (items.length === 0) return;
6335
6389
  const ids = new Set(items.map((item) => item.id));
6336
6390
  setExpanded((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => ids.has(id))));
@@ -6473,9 +6527,9 @@ function ChatPanel({
6473
6527
  queueItems = [],
6474
6528
  onRemoveQueueItem
6475
6529
  }) {
6476
- const listRef = React40.useRef(null);
6477
- const [nearBottom, setNearBottom] = React40.useState(true);
6478
- const handleSend = React40.useCallback(
6530
+ const listRef = React41.useRef(null);
6531
+ const [nearBottom, setNearBottom] = React41.useState(true);
6532
+ const handleSend = React41.useCallback(
6479
6533
  async (text, composerAttachments) => {
6480
6534
  const all = composerAttachments ?? attachments;
6481
6535
  await onSend(text, all.length > 0 ? all : void 0);
@@ -6489,7 +6543,7 @@ function ChatPanel({
6489
6543
  },
6490
6544
  [attachments, nearBottom, onClearAttachments, onSend]
6491
6545
  );
6492
- const handleScrollToBottom = React40.useCallback(() => {
6546
+ const handleScrollToBottom = React41.useCallback(() => {
6493
6547
  var _a;
6494
6548
  (_a = listRef.current) == null ? void 0 : _a.scrollToBottom({ animated: true });
6495
6549
  }, []);
@@ -6565,7 +6619,7 @@ function ChatPanel({
6565
6619
  }
6566
6620
 
6567
6621
  // src/components/dialogs/ConfirmMergeRequestDialog.tsx
6568
- var React41 = __toESM(require("react"));
6622
+ var React42 = __toESM(require("react"));
6569
6623
  var import_react_native54 = require("react-native");
6570
6624
 
6571
6625
  // src/components/primitives/Modal.tsx
@@ -6613,14 +6667,14 @@ function ConfirmMergeRequestDialog({
6613
6667
  onTestFirst
6614
6668
  }) {
6615
6669
  const theme = useTheme();
6616
- const close = React41.useCallback(() => onOpenChange(false), [onOpenChange]);
6670
+ const close = React42.useCallback(() => onOpenChange(false), [onOpenChange]);
6617
6671
  const canConfirm = Boolean(mergeRequest) && !approveDisabled;
6618
- const handleConfirm = React41.useCallback(() => {
6672
+ const handleConfirm = React42.useCallback(() => {
6619
6673
  if (!mergeRequest) return;
6620
6674
  onOpenChange(false);
6621
6675
  void onConfirm();
6622
6676
  }, [mergeRequest, onConfirm, onOpenChange]);
6623
- const handleTestFirst = React41.useCallback(() => {
6677
+ const handleTestFirst = React42.useCallback(() => {
6624
6678
  if (!mergeRequest) return;
6625
6679
  onOpenChange(false);
6626
6680
  void onTestFirst(mergeRequest);
@@ -6769,7 +6823,7 @@ function ConfirmMergeFlow({
6769
6823
  }
6770
6824
 
6771
6825
  // src/studio/hooks/useOptimisticChatMessages.ts
6772
- var React42 = __toESM(require("react"));
6826
+ var React43 = __toESM(require("react"));
6773
6827
  function makeOptimisticId() {
6774
6828
  return `optimistic:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 10)}`;
6775
6829
  }
@@ -6808,11 +6862,11 @@ function useOptimisticChatMessages({
6808
6862
  chatMessages,
6809
6863
  onSendChat
6810
6864
  }) {
6811
- const [optimisticChat, setOptimisticChat] = React42.useState([]);
6812
- React42.useEffect(() => {
6865
+ const [optimisticChat, setOptimisticChat] = React43.useState([]);
6866
+ React43.useEffect(() => {
6813
6867
  setOptimisticChat([]);
6814
6868
  }, [threadId]);
6815
- const messages = React42.useMemo(() => {
6869
+ const messages = React43.useMemo(() => {
6816
6870
  if (!optimisticChat || optimisticChat.length === 0) return chatMessages;
6817
6871
  const unresolved = optimisticChat.filter((o) => !isOptimisticResolvedByServer(chatMessages, o));
6818
6872
  if (unresolved.length === 0) return chatMessages;
@@ -6828,7 +6882,7 @@ function useOptimisticChatMessages({
6828
6882
  merged.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
6829
6883
  return merged;
6830
6884
  }, [chatMessages, optimisticChat]);
6831
- React42.useEffect(() => {
6885
+ React43.useEffect(() => {
6832
6886
  if (optimisticChat.length === 0) return;
6833
6887
  setOptimisticChat((prev) => {
6834
6888
  if (prev.length === 0) return prev;
@@ -6836,7 +6890,7 @@ function useOptimisticChatMessages({
6836
6890
  return next.length === prev.length ? prev : next;
6837
6891
  });
6838
6892
  }, [chatMessages, optimisticChat.length]);
6839
- const onSend = React42.useCallback(
6893
+ const onSend = React43.useCallback(
6840
6894
  async (text, attachments) => {
6841
6895
  if (shouldForkOnEdit || disableOptimistic) {
6842
6896
  await onSendChat(text, attachments);
@@ -6892,15 +6946,17 @@ function StudioOverlay({
6892
6946
  }) {
6893
6947
  const theme = useTheme();
6894
6948
  const { width } = (0, import_react_native55.useWindowDimensions)();
6895
- const [sheetOpen, setSheetOpen] = React43.useState(false);
6896
- const sheetOpenRef = React43.useRef(sheetOpen);
6897
- const [activePage, setActivePage] = React43.useState("preview");
6898
- const [drawing, setDrawing] = React43.useState(false);
6899
- const [chatAttachments, setChatAttachments] = React43.useState([]);
6900
- const [commentsAppId, setCommentsAppId] = React43.useState(null);
6901
- const [commentsCount, setCommentsCount] = React43.useState(null);
6949
+ const [sheetOpen, setSheetOpen] = React44.useState(false);
6950
+ const sheetOpenRef = React44.useRef(sheetOpen);
6951
+ const [activePage, setActivePage] = React44.useState("preview");
6952
+ const [drawing, setDrawing] = React44.useState(false);
6953
+ const [chatAttachments, setChatAttachments] = React44.useState([]);
6954
+ const [commentsAppId, setCommentsAppId] = React44.useState(null);
6955
+ const [commentsCount, setCommentsCount] = React44.useState(null);
6902
6956
  const threadId = (app == null ? void 0 : app.threadId) ?? null;
6903
- const disableOptimistic = Boolean(chatQueueItems && chatQueueItems.length > 0) || (app == null ? void 0 : app.status) === "editing";
6957
+ const isForking = chatForking || (app == null ? void 0 : app.status) === "forking";
6958
+ const queueItemsForChat = isForking ? [] : chatQueueItems;
6959
+ const disableOptimistic = Boolean(queueItemsForChat && queueItemsForChat.length > 0) || (app == null ? void 0 : app.status) === "editing";
6904
6960
  const optimistic = useOptimisticChatMessages({
6905
6961
  threadId,
6906
6962
  shouldForkOnEdit,
@@ -6908,24 +6964,24 @@ function StudioOverlay({
6908
6964
  chatMessages,
6909
6965
  onSendChat
6910
6966
  });
6911
- const [confirmMrId, setConfirmMrId] = React43.useState(null);
6912
- const confirmMr = React43.useMemo(
6967
+ const [confirmMrId, setConfirmMrId] = React44.useState(null);
6968
+ const confirmMr = React44.useMemo(
6913
6969
  () => confirmMrId ? incomingMergeRequests.find((m) => m.id === confirmMrId) ?? null : null,
6914
6970
  [confirmMrId, incomingMergeRequests]
6915
6971
  );
6916
- const handleSheetOpenChange = React43.useCallback((open) => {
6972
+ const handleSheetOpenChange = React44.useCallback((open) => {
6917
6973
  setSheetOpen(open);
6918
6974
  if (!open) import_react_native55.Keyboard.dismiss();
6919
6975
  }, []);
6920
- const closeSheet = React43.useCallback(() => {
6976
+ const closeSheet = React44.useCallback(() => {
6921
6977
  handleSheetOpenChange(false);
6922
6978
  }, [handleSheetOpenChange]);
6923
- const openSheet = React43.useCallback(() => setSheetOpen(true), []);
6924
- const goToChat = React43.useCallback(() => {
6979
+ const openSheet = React44.useCallback(() => setSheetOpen(true), []);
6980
+ const goToChat = React44.useCallback(() => {
6925
6981
  setActivePage("chat");
6926
6982
  openSheet();
6927
6983
  }, [openSheet]);
6928
- const backToPreview = React43.useCallback(() => {
6984
+ const backToPreview = React44.useCallback(() => {
6929
6985
  if (import_react_native55.Platform.OS !== "ios") {
6930
6986
  import_react_native55.Keyboard.dismiss();
6931
6987
  setActivePage("preview");
@@ -6943,11 +6999,11 @@ function StudioOverlay({
6943
6999
  const t = setTimeout(finalize, 350);
6944
7000
  import_react_native55.Keyboard.dismiss();
6945
7001
  }, []);
6946
- const startDraw = React43.useCallback(() => {
7002
+ const startDraw = React44.useCallback(() => {
6947
7003
  setDrawing(true);
6948
7004
  closeSheet();
6949
7005
  }, [closeSheet]);
6950
- const handleDrawCapture = React43.useCallback(
7006
+ const handleDrawCapture = React44.useCallback(
6951
7007
  (dataUrl) => {
6952
7008
  setChatAttachments((prev) => [...prev, dataUrl]);
6953
7009
  setDrawing(false);
@@ -6956,7 +7012,7 @@ function StudioOverlay({
6956
7012
  },
6957
7013
  [openSheet]
6958
7014
  );
6959
- const toggleSheet = React43.useCallback(async () => {
7015
+ const toggleSheet = React44.useCallback(async () => {
6960
7016
  if (!sheetOpen) {
6961
7017
  const shouldExitTest = Boolean(testingMrId) || isTesting;
6962
7018
  if (shouldExitTest) {
@@ -6968,7 +7024,7 @@ function StudioOverlay({
6968
7024
  closeSheet();
6969
7025
  }
6970
7026
  }, [closeSheet, isTesting, onRestoreBase, sheetOpen, testingMrId]);
6971
- const handleTestMr = React43.useCallback(
7027
+ const handleTestMr = React44.useCallback(
6972
7028
  async (mr) => {
6973
7029
  if (!onTestMr) return;
6974
7030
  await onTestMr(mr);
@@ -6976,10 +7032,10 @@ function StudioOverlay({
6976
7032
  },
6977
7033
  [closeSheet, onTestMr]
6978
7034
  );
6979
- React43.useEffect(() => {
7035
+ React44.useEffect(() => {
6980
7036
  sheetOpenRef.current = sheetOpen;
6981
7037
  }, [sheetOpen]);
6982
- React43.useEffect(() => {
7038
+ React44.useEffect(() => {
6983
7039
  const poller = (0, import_studio_control.startStudioControlPolling)((action) => {
6984
7040
  if (action === "show" && !sheetOpenRef.current) openSheet();
6985
7041
  if (action === "hide" && sheetOpenRef.current) closeSheet();
@@ -6987,7 +7043,7 @@ function StudioOverlay({
6987
7043
  }, studioControlOptions);
6988
7044
  return () => poller.stop();
6989
7045
  }, [closeSheet, openSheet, studioControlOptions, toggleSheet]);
6990
- React43.useEffect(() => {
7046
+ React44.useEffect(() => {
6991
7047
  void (0, import_studio_control.publishComergeStudioUIState)(sheetOpen, studioControlOptions);
6992
7048
  }, [sheetOpen, studioControlOptions]);
6993
7049
  return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_jsx_runtime58.Fragment, { children: [
@@ -7042,7 +7098,7 @@ function StudioOverlay({
7042
7098
  onNavigateHome,
7043
7099
  onStartDraw: startDraw,
7044
7100
  onSend: optimistic.onSend,
7045
- queueItems: chatQueueItems,
7101
+ queueItems: queueItemsForChat,
7046
7102
  onRemoveQueueItem
7047
7103
  }
7048
7104
  )
@@ -7094,7 +7150,7 @@ function StudioOverlay({
7094
7150
  }
7095
7151
 
7096
7152
  // src/studio/hooks/useEditQueue.ts
7097
- var React44 = __toESM(require("react"));
7153
+ var React45 = __toESM(require("react"));
7098
7154
 
7099
7155
  // src/data/apps/edit-queue/remote.ts
7100
7156
  var EditQueueRemoteDataSourceImpl = class extends BaseRemote {
@@ -7205,17 +7261,17 @@ var editQueueRepository = new EditQueueRepositoryImpl(
7205
7261
 
7206
7262
  // src/studio/hooks/useEditQueue.ts
7207
7263
  function useEditQueue(appId) {
7208
- const [items, setItems] = React44.useState([]);
7209
- const [loading, setLoading] = React44.useState(false);
7210
- const [error, setError] = React44.useState(null);
7211
- const activeRequestIdRef = React44.useRef(0);
7264
+ const [items, setItems] = React45.useState([]);
7265
+ const [loading, setLoading] = React45.useState(false);
7266
+ const [error, setError] = React45.useState(null);
7267
+ const activeRequestIdRef = React45.useRef(0);
7212
7268
  const foregroundSignal = useForegroundSignal(Boolean(appId));
7213
- const upsertSorted = React44.useCallback((prev, nextItem) => {
7269
+ const upsertSorted = React45.useCallback((prev, nextItem) => {
7214
7270
  const next = prev.some((x) => x.id === nextItem.id) ? prev.map((x) => x.id === nextItem.id ? nextItem : x) : [...prev, nextItem];
7215
7271
  next.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
7216
7272
  return next;
7217
7273
  }, []);
7218
- const refetch = React44.useCallback(async () => {
7274
+ const refetch = React45.useCallback(async () => {
7219
7275
  if (!appId) {
7220
7276
  setItems([]);
7221
7277
  return;
@@ -7235,10 +7291,10 @@ function useEditQueue(appId) {
7235
7291
  if (activeRequestIdRef.current === requestId) setLoading(false);
7236
7292
  }
7237
7293
  }, [appId]);
7238
- React44.useEffect(() => {
7294
+ React45.useEffect(() => {
7239
7295
  void refetch();
7240
7296
  }, [refetch]);
7241
- React44.useEffect(() => {
7297
+ React45.useEffect(() => {
7242
7298
  if (!appId) return;
7243
7299
  const unsubscribe = editQueueRepository.subscribeEditQueue(appId, {
7244
7300
  onInsert: (item) => setItems((prev) => upsertSorted(prev, item)),
@@ -7247,7 +7303,7 @@ function useEditQueue(appId) {
7247
7303
  });
7248
7304
  return unsubscribe;
7249
7305
  }, [appId, upsertSorted, foregroundSignal]);
7250
- React44.useEffect(() => {
7306
+ React45.useEffect(() => {
7251
7307
  if (!appId) return;
7252
7308
  if (foregroundSignal <= 0) return;
7253
7309
  void refetch();
@@ -7256,16 +7312,16 @@ function useEditQueue(appId) {
7256
7312
  }
7257
7313
 
7258
7314
  // src/studio/hooks/useEditQueueActions.ts
7259
- var React45 = __toESM(require("react"));
7315
+ var React46 = __toESM(require("react"));
7260
7316
  function useEditQueueActions(appId) {
7261
- const update = React45.useCallback(
7317
+ const update = React46.useCallback(
7262
7318
  async (queueItemId, payload) => {
7263
7319
  if (!appId) return;
7264
7320
  await editQueueRepository.update(appId, queueItemId, payload);
7265
7321
  },
7266
7322
  [appId]
7267
7323
  );
7268
- const cancel = React45.useCallback(
7324
+ const cancel = React46.useCallback(
7269
7325
  async (queueItemId) => {
7270
7326
  if (!appId) return;
7271
7327
  await editQueueRepository.cancel(appId, queueItemId);
@@ -7287,16 +7343,16 @@ function ComergeStudio({
7287
7343
  studioControlOptions,
7288
7344
  embeddedBaseBundles
7289
7345
  }) {
7290
- const [activeAppId, setActiveAppId] = React46.useState(appId);
7291
- const [runtimeAppId, setRuntimeAppId] = React46.useState(appId);
7292
- const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React46.useState(null);
7293
- const platform = React46.useMemo(() => import_react_native56.Platform.OS === "ios" ? "ios" : "android", []);
7294
- React46.useEffect(() => {
7346
+ const [activeAppId, setActiveAppId] = React47.useState(appId);
7347
+ const [runtimeAppId, setRuntimeAppId] = React47.useState(appId);
7348
+ const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React47.useState(null);
7349
+ const platform = React47.useMemo(() => import_react_native56.Platform.OS === "ios" ? "ios" : "android", []);
7350
+ React47.useEffect(() => {
7295
7351
  setActiveAppId(appId);
7296
7352
  setRuntimeAppId(appId);
7297
7353
  setPendingRuntimeTargetAppId(null);
7298
7354
  }, [appId]);
7299
- const captureTargetRef = React46.useRef(null);
7355
+ const captureTargetRef = React47.useRef(null);
7300
7356
  return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(StudioBootstrap, { clientKey: clientKey2, fallback: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_react_native56.View, { style: { flex: 1 } }), children: ({ userId }) => /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_bottom_sheet6.BottomSheetModalProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(LiquidGlassResetProvider, { resetTriggers: [appId, activeAppId, runtimeAppId], children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
7301
7357
  ComergeStudioInner,
7302
7358
  {
@@ -7338,11 +7394,11 @@ function ComergeStudioInner({
7338
7394
  const { app, loading: appLoading } = useApp(activeAppId);
7339
7395
  const { app: runtimeAppFromHook } = useApp(runtimeAppId, { enabled: runtimeAppId !== activeAppId });
7340
7396
  const runtimeApp = runtimeAppId === activeAppId ? app : runtimeAppFromHook;
7341
- const sawEditingOnPendingTargetRef = React46.useRef(false);
7342
- React46.useEffect(() => {
7397
+ const sawEditingOnPendingTargetRef = React47.useRef(false);
7398
+ React47.useEffect(() => {
7343
7399
  sawEditingOnPendingTargetRef.current = false;
7344
7400
  }, [pendingRuntimeTargetAppId]);
7345
- React46.useEffect(() => {
7401
+ React47.useEffect(() => {
7346
7402
  if (!pendingRuntimeTargetAppId) return;
7347
7403
  if (activeAppId !== pendingRuntimeTargetAppId) return;
7348
7404
  if ((app == null ? void 0 : app.status) === "editing") {
@@ -7360,13 +7416,13 @@ function ComergeStudioInner({
7360
7416
  canRequestLatest: (runtimeApp == null ? void 0 : runtimeApp.status) === "ready",
7361
7417
  embeddedBaseBundles
7362
7418
  });
7363
- const sawEditingOnActiveAppRef = React46.useRef(false);
7364
- const [showPostEditPreparing, setShowPostEditPreparing] = React46.useState(false);
7365
- React46.useEffect(() => {
7419
+ const sawEditingOnActiveAppRef = React47.useRef(false);
7420
+ const [showPostEditPreparing, setShowPostEditPreparing] = React47.useState(false);
7421
+ React47.useEffect(() => {
7366
7422
  sawEditingOnActiveAppRef.current = false;
7367
7423
  setShowPostEditPreparing(false);
7368
7424
  }, [activeAppId]);
7369
- React46.useEffect(() => {
7425
+ React47.useEffect(() => {
7370
7426
  if (!(app == null ? void 0 : app.id)) return;
7371
7427
  if (app.status === "editing") {
7372
7428
  sawEditingOnActiveAppRef.current = true;
@@ -7378,7 +7434,7 @@ function ComergeStudioInner({
7378
7434
  sawEditingOnActiveAppRef.current = false;
7379
7435
  }
7380
7436
  }, [app == null ? void 0 : app.id, app == null ? void 0 : app.status]);
7381
- React46.useEffect(() => {
7437
+ React47.useEffect(() => {
7382
7438
  if (!showPostEditPreparing) return;
7383
7439
  const stillProcessingBaseBundle = bundle.loading && bundle.loadingMode === "base" && !bundle.isTesting;
7384
7440
  if (!stillProcessingBaseBundle) {
@@ -7389,19 +7445,19 @@ function ComergeStudioInner({
7389
7445
  const thread = useThreadMessages(threadId);
7390
7446
  const editQueue = useEditQueue(activeAppId);
7391
7447
  const editQueueActions = useEditQueueActions(activeAppId);
7392
- const [lastEditQueueInfo, setLastEditQueueInfo] = React46.useState(null);
7393
- const lastEditQueueInfoRef = React46.useRef(null);
7394
- const [suppressQueueUntilResponse, setSuppressQueueUntilResponse] = React46.useState(false);
7448
+ const [lastEditQueueInfo, setLastEditQueueInfo] = React47.useState(null);
7449
+ const lastEditQueueInfoRef = React47.useRef(null);
7450
+ const [suppressQueueUntilResponse, setSuppressQueueUntilResponse] = React47.useState(false);
7395
7451
  const mergeRequests = useMergeRequests({ appId: activeAppId });
7396
- const hasOpenOutgoingMr = React46.useMemo(() => {
7452
+ const hasOpenOutgoingMr = React47.useMemo(() => {
7397
7453
  return mergeRequests.lists.outgoing.some((mr) => mr.status === "open");
7398
7454
  }, [mergeRequests.lists.outgoing]);
7399
- const incomingReviewMrs = React46.useMemo(() => {
7455
+ const incomingReviewMrs = React47.useMemo(() => {
7400
7456
  if (!userId) return mergeRequests.lists.incoming;
7401
7457
  return mergeRequests.lists.incoming.filter((mr) => mr.createdBy !== userId);
7402
7458
  }, [mergeRequests.lists.incoming, userId]);
7403
7459
  const uploader = useAttachmentUpload();
7404
- const updateLastEditQueueInfo = React46.useCallback(
7460
+ const updateLastEditQueueInfo = React47.useCallback(
7405
7461
  (info) => {
7406
7462
  lastEditQueueInfoRef.current = info;
7407
7463
  setLastEditQueueInfo(info);
@@ -7442,20 +7498,20 @@ function ComergeStudioInner({
7442
7498
  }
7443
7499
  });
7444
7500
  const chatSendDisabled = false;
7445
- const [processingMrId, setProcessingMrId] = React46.useState(null);
7446
- const [testingMrId, setTestingMrId] = React46.useState(null);
7447
- const chatShowTypingIndicator = React46.useMemo(() => {
7501
+ const [processingMrId, setProcessingMrId] = React47.useState(null);
7502
+ const [testingMrId, setTestingMrId] = React47.useState(null);
7503
+ const chatShowTypingIndicator = React47.useMemo(() => {
7448
7504
  var _a;
7449
7505
  if (!thread.raw || thread.raw.length === 0) return false;
7450
7506
  const last = thread.raw[thread.raw.length - 1];
7451
7507
  const payloadType = typeof ((_a = last.payload) == null ? void 0 : _a.type) === "string" ? String(last.payload.type) : void 0;
7452
7508
  return payloadType !== "outcome";
7453
7509
  }, [thread.raw]);
7454
- React46.useEffect(() => {
7510
+ React47.useEffect(() => {
7455
7511
  updateLastEditQueueInfo(null);
7456
7512
  setSuppressQueueUntilResponse(false);
7457
7513
  }, [activeAppId, updateLastEditQueueInfo]);
7458
- React46.useEffect(() => {
7514
+ React47.useEffect(() => {
7459
7515
  if (!(lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId)) return;
7460
7516
  const stillPresent = editQueue.items.some((item) => item.id === lastEditQueueInfo.queueItemId);
7461
7517
  if (!stillPresent) {
@@ -7463,7 +7519,7 @@ function ComergeStudioInner({
7463
7519
  setSuppressQueueUntilResponse(false);
7464
7520
  }
7465
7521
  }, [editQueue.items, lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId]);
7466
- const chatQueueItems = React46.useMemo(() => {
7522
+ const chatQueueItems = React47.useMemo(() => {
7467
7523
  var _a;
7468
7524
  if (suppressQueueUntilResponse && editQueue.items.length <= 1) {
7469
7525
  return [];