@comergehq/studio 0.1.17 → 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
 
@@ -2032,8 +2032,8 @@ function RuntimeRenderer({
2032
2032
  }
2033
2033
 
2034
2034
  // src/studio/ui/StudioOverlay.tsx
2035
- import * as React43 from "react";
2036
- 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";
2037
2037
 
2038
2038
  // src/components/studio-sheet/StudioBottomSheet.tsx
2039
2039
  import * as React12 from "react";
@@ -3230,7 +3230,8 @@ import {
3230
3230
  Play,
3231
3231
  Send,
3232
3232
  X as X2,
3233
- Check as Check2
3233
+ Check as Check2,
3234
+ Share2
3234
3235
  } from "lucide-react-native";
3235
3236
  import { jsx as jsx16 } from "react/jsx-runtime";
3236
3237
  function useResolvedIconColor(token) {
@@ -3270,6 +3271,7 @@ var IconSend = makeIcon(Send);
3270
3271
  var IconPlay = makeIcon(Play);
3271
3272
  var IconArrowDown = makeIcon(ArrowDown);
3272
3273
  var IconApprove = makeIcon(Check2);
3274
+ var IconShare = makeIcon(Share2);
3273
3275
 
3274
3276
  // src/components/chat/ChatComposer.tsx
3275
3277
  import { jsx as jsx17, jsxs as jsxs8 } from "react/jsx-runtime";
@@ -3986,7 +3988,8 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3986
3988
  }
3987
3989
 
3988
3990
  // src/studio/ui/PreviewPanel.tsx
3989
- 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";
3990
3993
 
3991
3994
  // src/components/preview/PreviewPage.tsx
3992
3995
  import { View as View15 } from "react-native";
@@ -4136,7 +4139,14 @@ function StudioSheetHeaderIconButton({
4136
4139
 
4137
4140
  // src/studio/ui/preview-panel/PreviewPanelHeader.tsx
4138
4141
  import { jsx as jsx24, jsxs as jsxs13 } from "react/jsx-runtime";
4139
- function PreviewPanelHeader({ isOwner, onClose, onNavigateHome, onGoToChat }) {
4142
+ function PreviewPanelHeader({
4143
+ isOwner,
4144
+ isPublic,
4145
+ onClose,
4146
+ onNavigateHome,
4147
+ onGoToChat,
4148
+ onShare
4149
+ }) {
4140
4150
  return /* @__PURE__ */ jsx24(
4141
4151
  StudioSheetHeader,
4142
4152
  {
@@ -4154,6 +4164,17 @@ function PreviewPanelHeader({ isOwner, onClose, onNavigateHome, onGoToChat }) {
4154
4164
  children: /* @__PURE__ */ jsx24(IconChat, { size: 20, colorToken: "onPrimary" })
4155
4165
  }
4156
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,
4157
4178
  /* @__PURE__ */ jsx24(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", appearance: "glass", intent: "primary", children: /* @__PURE__ */ jsx24(IconClose, { size: 20, colorToken: "onPrimary" }) })
4158
4179
  ] })
4159
4180
  }
@@ -5826,6 +5847,27 @@ function PreviewPanel({
5826
5847
  onOpenComments,
5827
5848
  commentCountOverride
5828
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]);
5829
5871
  const { imageUrl, imageLoaded, setImageLoaded, creator, insights, stats, showProcessing, canSubmitMergeRequest } = usePreviewPanelData({
5830
5872
  app,
5831
5873
  isOwner,
@@ -5833,7 +5875,17 @@ function PreviewPanel({
5833
5875
  onOpenComments,
5834
5876
  commentCountOverride
5835
5877
  });
5836
- 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
+ );
5837
5889
  if (loading || !app) {
5838
5890
  return /* @__PURE__ */ jsx45(PreviewPage, { header, children: /* @__PURE__ */ jsxs26(View33, { style: { flex: 1, justifyContent: "center", alignItems: "center", padding: 24 }, children: [
5839
5891
  /* @__PURE__ */ jsx45(ActivityIndicator7, {}),
@@ -5893,16 +5945,16 @@ function PreviewPanel({
5893
5945
  }
5894
5946
 
5895
5947
  // src/studio/ui/ChatPanel.tsx
5896
- import * as React40 from "react";
5948
+ import * as React41 from "react";
5897
5949
  import { ActivityIndicator as ActivityIndicator9, View as View42 } from "react-native";
5898
5950
 
5899
5951
  // src/components/chat/ChatPage.tsx
5900
- import * as React37 from "react";
5901
- 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";
5902
5954
  import { useSafeAreaInsets as useSafeAreaInsets4 } from "react-native-safe-area-context";
5903
5955
 
5904
5956
  // src/components/chat/ChatMessageList.tsx
5905
- import * as React36 from "react";
5957
+ import * as React37 from "react";
5906
5958
  import { View as View36 } from "react-native";
5907
5959
  import { BottomSheetFlatList } from "@gorhom/bottom-sheet";
5908
5960
 
@@ -5948,17 +6000,17 @@ function ChatMessageBubble({ message, renderContent, style }) {
5948
6000
  }
5949
6001
 
5950
6002
  // src/components/chat/TypingIndicator.tsx
5951
- import * as React35 from "react";
6003
+ import * as React36 from "react";
5952
6004
  import { Animated as Animated10, View as View35 } from "react-native";
5953
6005
  import { jsx as jsx47 } from "react/jsx-runtime";
5954
6006
  function TypingIndicator({ style }) {
5955
6007
  const theme = useTheme();
5956
6008
  const dotColor = theme.colors.textSubtle;
5957
- const anims = React35.useMemo(
6009
+ const anims = React36.useMemo(
5958
6010
  () => [new Animated10.Value(0.3), new Animated10.Value(0.3), new Animated10.Value(0.3)],
5959
6011
  []
5960
6012
  );
5961
- React35.useEffect(() => {
6013
+ React36.useEffect(() => {
5962
6014
  const loops = [];
5963
6015
  anims.forEach((a, idx) => {
5964
6016
  const seq = Animated10.sequence([
@@ -5992,7 +6044,7 @@ function TypingIndicator({ style }) {
5992
6044
 
5993
6045
  // src/components/chat/ChatMessageList.tsx
5994
6046
  import { jsx as jsx48, jsxs as jsxs28 } from "react/jsx-runtime";
5995
- var ChatMessageList = React36.forwardRef(
6047
+ var ChatMessageList = React37.forwardRef(
5996
6048
  ({
5997
6049
  messages,
5998
6050
  showTypingIndicator = false,
@@ -6003,20 +6055,20 @@ var ChatMessageList = React36.forwardRef(
6003
6055
  nearBottomThreshold = 200
6004
6056
  }, ref) => {
6005
6057
  const theme = useTheme();
6006
- const listRef = React36.useRef(null);
6007
- const nearBottomRef = React36.useRef(true);
6008
- const initialScrollDoneRef = React36.useRef(false);
6009
- const lastMessageIdRef = React36.useRef(null);
6010
- 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(() => {
6011
6063
  return [...messages].reverse();
6012
6064
  }, [messages]);
6013
- const scrollToBottom = React36.useCallback((options) => {
6065
+ const scrollToBottom = React37.useCallback((options) => {
6014
6066
  var _a;
6015
6067
  const animated = (options == null ? void 0 : options.animated) ?? true;
6016
6068
  (_a = listRef.current) == null ? void 0 : _a.scrollToOffset({ offset: 0, animated });
6017
6069
  }, []);
6018
- React36.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
6019
- const handleScroll = React36.useCallback(
6070
+ React37.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
6071
+ const handleScroll = React37.useCallback(
6020
6072
  (e) => {
6021
6073
  const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;
6022
6074
  const distanceFromBottom = Math.max(contentOffset.y - Math.max(bottomInset, 0), 0);
@@ -6028,7 +6080,7 @@ var ChatMessageList = React36.forwardRef(
6028
6080
  },
6029
6081
  [bottomInset, nearBottomThreshold, onNearBottomChange]
6030
6082
  );
6031
- React36.useEffect(() => {
6083
+ React37.useEffect(() => {
6032
6084
  if (!initialScrollDoneRef.current) return;
6033
6085
  const lastId = messages.length > 0 ? messages[messages.length - 1].id : null;
6034
6086
  const prevLastId = lastMessageIdRef.current;
@@ -6038,7 +6090,7 @@ var ChatMessageList = React36.forwardRef(
6038
6090
  const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
6039
6091
  return () => cancelAnimationFrame(id);
6040
6092
  }, [messages, scrollToBottom]);
6041
- React36.useEffect(() => {
6093
+ React37.useEffect(() => {
6042
6094
  if (showTypingIndicator && nearBottomRef.current) {
6043
6095
  const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
6044
6096
  return () => cancelAnimationFrame(id);
@@ -6101,11 +6153,11 @@ function ChatPage({
6101
6153
  }) {
6102
6154
  const theme = useTheme();
6103
6155
  const insets = useSafeAreaInsets4();
6104
- const [composerHeight, setComposerHeight] = React37.useState(0);
6105
- const [composerTopHeight, setComposerTopHeight] = React37.useState(0);
6106
- const [keyboardVisible, setKeyboardVisible] = React37.useState(false);
6107
- React37.useEffect(() => {
6108
- 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;
6109
6161
  const show = Keyboard4.addListener("keyboardWillShow", () => setKeyboardVisible(true));
6110
6162
  const hide = Keyboard4.addListener("keyboardWillHide", () => setKeyboardVisible(false));
6111
6163
  return () => {
@@ -6113,20 +6165,20 @@ function ChatPage({
6113
6165
  hide.remove();
6114
6166
  };
6115
6167
  }, []);
6116
- 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;
6117
6169
  const totalComposerHeight = composerHeight + composerTopHeight;
6118
6170
  const overlayBottom = totalComposerHeight + footerBottomPadding + theme.spacing.lg;
6119
6171
  const bottomInset = totalComposerHeight + footerBottomPadding + theme.spacing.xl;
6120
- const resolvedOverlay = React37.useMemo(() => {
6172
+ const resolvedOverlay = React38.useMemo(() => {
6121
6173
  var _a;
6122
6174
  if (!overlay) return null;
6123
- if (!React37.isValidElement(overlay)) return overlay;
6175
+ if (!React38.isValidElement(overlay)) return overlay;
6124
6176
  const prevStyle = (_a = overlay.props) == null ? void 0 : _a.style;
6125
- return React37.cloneElement(overlay, {
6177
+ return React38.cloneElement(overlay, {
6126
6178
  style: [prevStyle, { bottom: overlayBottom }]
6127
6179
  });
6128
6180
  }, [overlay, overlayBottom]);
6129
- React37.useEffect(() => {
6181
+ React38.useEffect(() => {
6130
6182
  if (composerTop) return;
6131
6183
  setComposerTopHeight(0);
6132
6184
  }, [composerTop]);
@@ -6191,15 +6243,15 @@ function ChatPage({
6191
6243
  }
6192
6244
 
6193
6245
  // src/components/chat/ScrollToBottomButton.tsx
6194
- import * as React38 from "react";
6246
+ import * as React39 from "react";
6195
6247
  import { Pressable as Pressable12, View as View38 } from "react-native";
6196
6248
  import Animated11, { Easing as Easing2, useAnimatedStyle as useAnimatedStyle2, useSharedValue as useSharedValue2, withTiming as withTiming2 } from "react-native-reanimated";
6197
6249
  import { jsx as jsx50 } from "react/jsx-runtime";
6198
6250
  function ScrollToBottomButton({ visible, onPress, children, style }) {
6199
6251
  const theme = useTheme();
6200
6252
  const progress = useSharedValue2(visible ? 1 : 0);
6201
- const [pressed, setPressed] = React38.useState(false);
6202
- React38.useEffect(() => {
6253
+ const [pressed, setPressed] = React39.useState(false);
6254
+ React39.useEffect(() => {
6203
6255
  progress.value = withTiming2(visible ? 1 : 0, { duration: 200, easing: Easing2.out(Easing2.ease) });
6204
6256
  }, [progress, visible]);
6205
6257
  const animStyle = useAnimatedStyle2(() => ({
@@ -6334,16 +6386,16 @@ function ForkNoticeBanner({ isOwner = true, title, description, style }) {
6334
6386
  }
6335
6387
 
6336
6388
  // src/components/chat/ChatQueue.tsx
6337
- import * as React39 from "react";
6389
+ import * as React40 from "react";
6338
6390
  import { ActivityIndicator as ActivityIndicator8, Pressable as Pressable13, View as View41 } from "react-native";
6339
6391
  import { jsx as jsx53, jsxs as jsxs31 } from "react/jsx-runtime";
6340
6392
  function ChatQueue({ items, onRemove }) {
6341
6393
  const theme = useTheme();
6342
- const [expanded, setExpanded] = React39.useState({});
6343
- const [canExpand, setCanExpand] = React39.useState({});
6344
- const [collapsedText, setCollapsedText] = React39.useState({});
6345
- const [removing, setRemoving] = React39.useState({});
6346
- 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) => {
6347
6399
  var _a, _b;
6348
6400
  const line1 = ((_a = lines[0]) == null ? void 0 : _a.text) ?? "";
6349
6401
  const line2 = ((_b = lines[1]) == null ? void 0 : _b.text) ?? "";
@@ -6359,7 +6411,7 @@ function ChatQueue({ items, onRemove }) {
6359
6411
  return `${line1}
6360
6412
  ${trimmedLine2}\u2026 `;
6361
6413
  }, []);
6362
- React39.useEffect(() => {
6414
+ React40.useEffect(() => {
6363
6415
  if (items.length === 0) return;
6364
6416
  const ids = new Set(items.map((item) => item.id));
6365
6417
  setExpanded((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => ids.has(id))));
@@ -6502,9 +6554,9 @@ function ChatPanel({
6502
6554
  queueItems = [],
6503
6555
  onRemoveQueueItem
6504
6556
  }) {
6505
- const listRef = React40.useRef(null);
6506
- const [nearBottom, setNearBottom] = React40.useState(true);
6507
- const handleSend = React40.useCallback(
6557
+ const listRef = React41.useRef(null);
6558
+ const [nearBottom, setNearBottom] = React41.useState(true);
6559
+ const handleSend = React41.useCallback(
6508
6560
  async (text, composerAttachments) => {
6509
6561
  const all = composerAttachments ?? attachments;
6510
6562
  await onSend(text, all.length > 0 ? all : void 0);
@@ -6518,7 +6570,7 @@ function ChatPanel({
6518
6570
  },
6519
6571
  [attachments, nearBottom, onClearAttachments, onSend]
6520
6572
  );
6521
- const handleScrollToBottom = React40.useCallback(() => {
6573
+ const handleScrollToBottom = React41.useCallback(() => {
6522
6574
  var _a;
6523
6575
  (_a = listRef.current) == null ? void 0 : _a.scrollToBottom({ animated: true });
6524
6576
  }, []);
@@ -6594,7 +6646,7 @@ function ChatPanel({
6594
6646
  }
6595
6647
 
6596
6648
  // src/components/dialogs/ConfirmMergeRequestDialog.tsx
6597
- import * as React41 from "react";
6649
+ import * as React42 from "react";
6598
6650
  import { Pressable as Pressable15, View as View44 } from "react-native";
6599
6651
 
6600
6652
  // src/components/primitives/Modal.tsx
@@ -6646,14 +6698,14 @@ function ConfirmMergeRequestDialog({
6646
6698
  onTestFirst
6647
6699
  }) {
6648
6700
  const theme = useTheme();
6649
- const close = React41.useCallback(() => onOpenChange(false), [onOpenChange]);
6701
+ const close = React42.useCallback(() => onOpenChange(false), [onOpenChange]);
6650
6702
  const canConfirm = Boolean(mergeRequest) && !approveDisabled;
6651
- const handleConfirm = React41.useCallback(() => {
6703
+ const handleConfirm = React42.useCallback(() => {
6652
6704
  if (!mergeRequest) return;
6653
6705
  onOpenChange(false);
6654
6706
  void onConfirm();
6655
6707
  }, [mergeRequest, onConfirm, onOpenChange]);
6656
- const handleTestFirst = React41.useCallback(() => {
6708
+ const handleTestFirst = React42.useCallback(() => {
6657
6709
  if (!mergeRequest) return;
6658
6710
  onOpenChange(false);
6659
6711
  void onTestFirst(mergeRequest);
@@ -6802,7 +6854,7 @@ function ConfirmMergeFlow({
6802
6854
  }
6803
6855
 
6804
6856
  // src/studio/hooks/useOptimisticChatMessages.ts
6805
- import * as React42 from "react";
6857
+ import * as React43 from "react";
6806
6858
  function makeOptimisticId() {
6807
6859
  return `optimistic:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 10)}`;
6808
6860
  }
@@ -6841,11 +6893,11 @@ function useOptimisticChatMessages({
6841
6893
  chatMessages,
6842
6894
  onSendChat
6843
6895
  }) {
6844
- const [optimisticChat, setOptimisticChat] = React42.useState([]);
6845
- React42.useEffect(() => {
6896
+ const [optimisticChat, setOptimisticChat] = React43.useState([]);
6897
+ React43.useEffect(() => {
6846
6898
  setOptimisticChat([]);
6847
6899
  }, [threadId]);
6848
- const messages = React42.useMemo(() => {
6900
+ const messages = React43.useMemo(() => {
6849
6901
  if (!optimisticChat || optimisticChat.length === 0) return chatMessages;
6850
6902
  const unresolved = optimisticChat.filter((o) => !isOptimisticResolvedByServer(chatMessages, o));
6851
6903
  if (unresolved.length === 0) return chatMessages;
@@ -6861,7 +6913,7 @@ function useOptimisticChatMessages({
6861
6913
  merged.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
6862
6914
  return merged;
6863
6915
  }, [chatMessages, optimisticChat]);
6864
- React42.useEffect(() => {
6916
+ React43.useEffect(() => {
6865
6917
  if (optimisticChat.length === 0) return;
6866
6918
  setOptimisticChat((prev) => {
6867
6919
  if (prev.length === 0) return prev;
@@ -6869,7 +6921,7 @@ function useOptimisticChatMessages({
6869
6921
  return next.length === prev.length ? prev : next;
6870
6922
  });
6871
6923
  }, [chatMessages, optimisticChat.length]);
6872
- const onSend = React42.useCallback(
6924
+ const onSend = React43.useCallback(
6873
6925
  async (text, attachments) => {
6874
6926
  if (shouldForkOnEdit || disableOptimistic) {
6875
6927
  await onSendChat(text, attachments);
@@ -6928,13 +6980,13 @@ function StudioOverlay({
6928
6980
  }) {
6929
6981
  const theme = useTheme();
6930
6982
  const { width } = useWindowDimensions4();
6931
- const [sheetOpen, setSheetOpen] = React43.useState(false);
6932
- const sheetOpenRef = React43.useRef(sheetOpen);
6933
- const [activePage, setActivePage] = React43.useState("preview");
6934
- const [drawing, setDrawing] = React43.useState(false);
6935
- const [chatAttachments, setChatAttachments] = React43.useState([]);
6936
- const [commentsAppId, setCommentsAppId] = React43.useState(null);
6937
- 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);
6938
6990
  const threadId = (app == null ? void 0 : app.threadId) ?? null;
6939
6991
  const isForking = chatForking || (app == null ? void 0 : app.status) === "forking";
6940
6992
  const queueItemsForChat = isForking ? [] : chatQueueItems;
@@ -6946,25 +6998,25 @@ function StudioOverlay({
6946
6998
  chatMessages,
6947
6999
  onSendChat
6948
7000
  });
6949
- const [confirmMrId, setConfirmMrId] = React43.useState(null);
6950
- const confirmMr = React43.useMemo(
7001
+ const [confirmMrId, setConfirmMrId] = React44.useState(null);
7002
+ const confirmMr = React44.useMemo(
6951
7003
  () => confirmMrId ? incomingMergeRequests.find((m) => m.id === confirmMrId) ?? null : null,
6952
7004
  [confirmMrId, incomingMergeRequests]
6953
7005
  );
6954
- const handleSheetOpenChange = React43.useCallback((open) => {
7006
+ const handleSheetOpenChange = React44.useCallback((open) => {
6955
7007
  setSheetOpen(open);
6956
7008
  if (!open) Keyboard5.dismiss();
6957
7009
  }, []);
6958
- const closeSheet = React43.useCallback(() => {
7010
+ const closeSheet = React44.useCallback(() => {
6959
7011
  handleSheetOpenChange(false);
6960
7012
  }, [handleSheetOpenChange]);
6961
- const openSheet = React43.useCallback(() => setSheetOpen(true), []);
6962
- const goToChat = React43.useCallback(() => {
7013
+ const openSheet = React44.useCallback(() => setSheetOpen(true), []);
7014
+ const goToChat = React44.useCallback(() => {
6963
7015
  setActivePage("chat");
6964
7016
  openSheet();
6965
7017
  }, [openSheet]);
6966
- const backToPreview = React43.useCallback(() => {
6967
- if (Platform10.OS !== "ios") {
7018
+ const backToPreview = React44.useCallback(() => {
7019
+ if (Platform11.OS !== "ios") {
6968
7020
  Keyboard5.dismiss();
6969
7021
  setActivePage("preview");
6970
7022
  return;
@@ -6981,11 +7033,11 @@ function StudioOverlay({
6981
7033
  const t = setTimeout(finalize, 350);
6982
7034
  Keyboard5.dismiss();
6983
7035
  }, []);
6984
- const startDraw = React43.useCallback(() => {
7036
+ const startDraw = React44.useCallback(() => {
6985
7037
  setDrawing(true);
6986
7038
  closeSheet();
6987
7039
  }, [closeSheet]);
6988
- const handleDrawCapture = React43.useCallback(
7040
+ const handleDrawCapture = React44.useCallback(
6989
7041
  (dataUrl) => {
6990
7042
  setChatAttachments((prev) => [...prev, dataUrl]);
6991
7043
  setDrawing(false);
@@ -6994,7 +7046,7 @@ function StudioOverlay({
6994
7046
  },
6995
7047
  [openSheet]
6996
7048
  );
6997
- const toggleSheet = React43.useCallback(async () => {
7049
+ const toggleSheet = React44.useCallback(async () => {
6998
7050
  if (!sheetOpen) {
6999
7051
  const shouldExitTest = Boolean(testingMrId) || isTesting;
7000
7052
  if (shouldExitTest) {
@@ -7006,7 +7058,7 @@ function StudioOverlay({
7006
7058
  closeSheet();
7007
7059
  }
7008
7060
  }, [closeSheet, isTesting, onRestoreBase, sheetOpen, testingMrId]);
7009
- const handleTestMr = React43.useCallback(
7061
+ const handleTestMr = React44.useCallback(
7010
7062
  async (mr) => {
7011
7063
  if (!onTestMr) return;
7012
7064
  await onTestMr(mr);
@@ -7014,10 +7066,10 @@ function StudioOverlay({
7014
7066
  },
7015
7067
  [closeSheet, onTestMr]
7016
7068
  );
7017
- React43.useEffect(() => {
7069
+ React44.useEffect(() => {
7018
7070
  sheetOpenRef.current = sheetOpen;
7019
7071
  }, [sheetOpen]);
7020
- React43.useEffect(() => {
7072
+ React44.useEffect(() => {
7021
7073
  const poller = startStudioControlPolling((action) => {
7022
7074
  if (action === "show" && !sheetOpenRef.current) openSheet();
7023
7075
  if (action === "hide" && sheetOpenRef.current) closeSheet();
@@ -7025,7 +7077,7 @@ function StudioOverlay({
7025
7077
  }, studioControlOptions);
7026
7078
  return () => poller.stop();
7027
7079
  }, [closeSheet, openSheet, studioControlOptions, toggleSheet]);
7028
- React43.useEffect(() => {
7080
+ React44.useEffect(() => {
7029
7081
  void publishComergeStudioUIState(sheetOpen, studioControlOptions);
7030
7082
  }, [sheetOpen, studioControlOptions]);
7031
7083
  return /* @__PURE__ */ jsxs35(Fragment6, { children: [
@@ -7132,7 +7184,7 @@ function StudioOverlay({
7132
7184
  }
7133
7185
 
7134
7186
  // src/studio/hooks/useEditQueue.ts
7135
- import * as React44 from "react";
7187
+ import * as React45 from "react";
7136
7188
 
7137
7189
  // src/data/apps/edit-queue/remote.ts
7138
7190
  var EditQueueRemoteDataSourceImpl = class extends BaseRemote {
@@ -7243,17 +7295,17 @@ var editQueueRepository = new EditQueueRepositoryImpl(
7243
7295
 
7244
7296
  // src/studio/hooks/useEditQueue.ts
7245
7297
  function useEditQueue(appId) {
7246
- const [items, setItems] = React44.useState([]);
7247
- const [loading, setLoading] = React44.useState(false);
7248
- const [error, setError] = React44.useState(null);
7249
- 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);
7250
7302
  const foregroundSignal = useForegroundSignal(Boolean(appId));
7251
- const upsertSorted = React44.useCallback((prev, nextItem) => {
7303
+ const upsertSorted = React45.useCallback((prev, nextItem) => {
7252
7304
  const next = prev.some((x) => x.id === nextItem.id) ? prev.map((x) => x.id === nextItem.id ? nextItem : x) : [...prev, nextItem];
7253
7305
  next.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
7254
7306
  return next;
7255
7307
  }, []);
7256
- const refetch = React44.useCallback(async () => {
7308
+ const refetch = React45.useCallback(async () => {
7257
7309
  if (!appId) {
7258
7310
  setItems([]);
7259
7311
  return;
@@ -7273,10 +7325,10 @@ function useEditQueue(appId) {
7273
7325
  if (activeRequestIdRef.current === requestId) setLoading(false);
7274
7326
  }
7275
7327
  }, [appId]);
7276
- React44.useEffect(() => {
7328
+ React45.useEffect(() => {
7277
7329
  void refetch();
7278
7330
  }, [refetch]);
7279
- React44.useEffect(() => {
7331
+ React45.useEffect(() => {
7280
7332
  if (!appId) return;
7281
7333
  const unsubscribe = editQueueRepository.subscribeEditQueue(appId, {
7282
7334
  onInsert: (item) => setItems((prev) => upsertSorted(prev, item)),
@@ -7285,7 +7337,7 @@ function useEditQueue(appId) {
7285
7337
  });
7286
7338
  return unsubscribe;
7287
7339
  }, [appId, upsertSorted, foregroundSignal]);
7288
- React44.useEffect(() => {
7340
+ React45.useEffect(() => {
7289
7341
  if (!appId) return;
7290
7342
  if (foregroundSignal <= 0) return;
7291
7343
  void refetch();
@@ -7294,16 +7346,16 @@ function useEditQueue(appId) {
7294
7346
  }
7295
7347
 
7296
7348
  // src/studio/hooks/useEditQueueActions.ts
7297
- import * as React45 from "react";
7349
+ import * as React46 from "react";
7298
7350
  function useEditQueueActions(appId) {
7299
- const update = React45.useCallback(
7351
+ const update = React46.useCallback(
7300
7352
  async (queueItemId, payload) => {
7301
7353
  if (!appId) return;
7302
7354
  await editQueueRepository.update(appId, queueItemId, payload);
7303
7355
  },
7304
7356
  [appId]
7305
7357
  );
7306
- const cancel = React45.useCallback(
7358
+ const cancel = React46.useCallback(
7307
7359
  async (queueItemId) => {
7308
7360
  if (!appId) return;
7309
7361
  await editQueueRepository.cancel(appId, queueItemId);
@@ -7325,16 +7377,16 @@ function ComergeStudio({
7325
7377
  studioControlOptions,
7326
7378
  embeddedBaseBundles
7327
7379
  }) {
7328
- const [activeAppId, setActiveAppId] = React46.useState(appId);
7329
- const [runtimeAppId, setRuntimeAppId] = React46.useState(appId);
7330
- const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React46.useState(null);
7331
- const platform = React46.useMemo(() => RNPlatform.OS === "ios" ? "ios" : "android", []);
7332
- 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(() => {
7333
7385
  setActiveAppId(appId);
7334
7386
  setRuntimeAppId(appId);
7335
7387
  setPendingRuntimeTargetAppId(null);
7336
7388
  }, [appId]);
7337
- const captureTargetRef = React46.useRef(null);
7389
+ const captureTargetRef = React47.useRef(null);
7338
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(
7339
7391
  ComergeStudioInner,
7340
7392
  {
@@ -7376,11 +7428,11 @@ function ComergeStudioInner({
7376
7428
  const { app, loading: appLoading } = useApp(activeAppId);
7377
7429
  const { app: runtimeAppFromHook } = useApp(runtimeAppId, { enabled: runtimeAppId !== activeAppId });
7378
7430
  const runtimeApp = runtimeAppId === activeAppId ? app : runtimeAppFromHook;
7379
- const sawEditingOnPendingTargetRef = React46.useRef(false);
7380
- React46.useEffect(() => {
7431
+ const sawEditingOnPendingTargetRef = React47.useRef(false);
7432
+ React47.useEffect(() => {
7381
7433
  sawEditingOnPendingTargetRef.current = false;
7382
7434
  }, [pendingRuntimeTargetAppId]);
7383
- React46.useEffect(() => {
7435
+ React47.useEffect(() => {
7384
7436
  if (!pendingRuntimeTargetAppId) return;
7385
7437
  if (activeAppId !== pendingRuntimeTargetAppId) return;
7386
7438
  if ((app == null ? void 0 : app.status) === "editing") {
@@ -7398,13 +7450,13 @@ function ComergeStudioInner({
7398
7450
  canRequestLatest: (runtimeApp == null ? void 0 : runtimeApp.status) === "ready",
7399
7451
  embeddedBaseBundles
7400
7452
  });
7401
- const sawEditingOnActiveAppRef = React46.useRef(false);
7402
- const [showPostEditPreparing, setShowPostEditPreparing] = React46.useState(false);
7403
- React46.useEffect(() => {
7453
+ const sawEditingOnActiveAppRef = React47.useRef(false);
7454
+ const [showPostEditPreparing, setShowPostEditPreparing] = React47.useState(false);
7455
+ React47.useEffect(() => {
7404
7456
  sawEditingOnActiveAppRef.current = false;
7405
7457
  setShowPostEditPreparing(false);
7406
7458
  }, [activeAppId]);
7407
- React46.useEffect(() => {
7459
+ React47.useEffect(() => {
7408
7460
  if (!(app == null ? void 0 : app.id)) return;
7409
7461
  if (app.status === "editing") {
7410
7462
  sawEditingOnActiveAppRef.current = true;
@@ -7416,7 +7468,7 @@ function ComergeStudioInner({
7416
7468
  sawEditingOnActiveAppRef.current = false;
7417
7469
  }
7418
7470
  }, [app == null ? void 0 : app.id, app == null ? void 0 : app.status]);
7419
- React46.useEffect(() => {
7471
+ React47.useEffect(() => {
7420
7472
  if (!showPostEditPreparing) return;
7421
7473
  const stillProcessingBaseBundle = bundle.loading && bundle.loadingMode === "base" && !bundle.isTesting;
7422
7474
  if (!stillProcessingBaseBundle) {
@@ -7427,19 +7479,19 @@ function ComergeStudioInner({
7427
7479
  const thread = useThreadMessages(threadId);
7428
7480
  const editQueue = useEditQueue(activeAppId);
7429
7481
  const editQueueActions = useEditQueueActions(activeAppId);
7430
- const [lastEditQueueInfo, setLastEditQueueInfo] = React46.useState(null);
7431
- const lastEditQueueInfoRef = React46.useRef(null);
7432
- 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);
7433
7485
  const mergeRequests = useMergeRequests({ appId: activeAppId });
7434
- const hasOpenOutgoingMr = React46.useMemo(() => {
7486
+ const hasOpenOutgoingMr = React47.useMemo(() => {
7435
7487
  return mergeRequests.lists.outgoing.some((mr) => mr.status === "open");
7436
7488
  }, [mergeRequests.lists.outgoing]);
7437
- const incomingReviewMrs = React46.useMemo(() => {
7489
+ const incomingReviewMrs = React47.useMemo(() => {
7438
7490
  if (!userId) return mergeRequests.lists.incoming;
7439
7491
  return mergeRequests.lists.incoming.filter((mr) => mr.createdBy !== userId);
7440
7492
  }, [mergeRequests.lists.incoming, userId]);
7441
7493
  const uploader = useAttachmentUpload();
7442
- const updateLastEditQueueInfo = React46.useCallback(
7494
+ const updateLastEditQueueInfo = React47.useCallback(
7443
7495
  (info) => {
7444
7496
  lastEditQueueInfoRef.current = info;
7445
7497
  setLastEditQueueInfo(info);
@@ -7480,20 +7532,20 @@ function ComergeStudioInner({
7480
7532
  }
7481
7533
  });
7482
7534
  const chatSendDisabled = false;
7483
- const [processingMrId, setProcessingMrId] = React46.useState(null);
7484
- const [testingMrId, setTestingMrId] = React46.useState(null);
7485
- const chatShowTypingIndicator = React46.useMemo(() => {
7535
+ const [processingMrId, setProcessingMrId] = React47.useState(null);
7536
+ const [testingMrId, setTestingMrId] = React47.useState(null);
7537
+ const chatShowTypingIndicator = React47.useMemo(() => {
7486
7538
  var _a;
7487
7539
  if (!thread.raw || thread.raw.length === 0) return false;
7488
7540
  const last = thread.raw[thread.raw.length - 1];
7489
7541
  const payloadType = typeof ((_a = last.payload) == null ? void 0 : _a.type) === "string" ? String(last.payload.type) : void 0;
7490
7542
  return payloadType !== "outcome";
7491
7543
  }, [thread.raw]);
7492
- React46.useEffect(() => {
7544
+ React47.useEffect(() => {
7493
7545
  updateLastEditQueueInfo(null);
7494
7546
  setSuppressQueueUntilResponse(false);
7495
7547
  }, [activeAppId, updateLastEditQueueInfo]);
7496
- React46.useEffect(() => {
7548
+ React47.useEffect(() => {
7497
7549
  if (!(lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId)) return;
7498
7550
  const stillPresent = editQueue.items.some((item) => item.id === lastEditQueueInfo.queueItemId);
7499
7551
  if (!stillPresent) {
@@ -7501,7 +7553,7 @@ function ComergeStudioInner({
7501
7553
  setSuppressQueueUntilResponse(false);
7502
7554
  }
7503
7555
  }, [editQueue.items, lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId]);
7504
- const chatQueueItems = React46.useMemo(() => {
7556
+ const chatQueueItems = React47.useMemo(() => {
7505
7557
  var _a;
7506
7558
  if (suppressQueueUntilResponse && editQueue.items.length <= 1) {
7507
7559
  return [];