@comergehq/studio 0.1.12 → 0.1.13

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 React42 from "react";
9
+ import * as React43 from "react";
10
10
  import { Platform as RNPlatform, View as View45 } from "react-native";
11
11
  import { BottomSheetModalProvider } from "@gorhom/bottom-sheet";
12
12
 
@@ -188,11 +188,11 @@ import * as React from "react";
188
188
  import axios from "axios";
189
189
 
190
190
  // src/core/services/http/baseUrl.ts
191
- var BASE_URL = "https://comerge.ai";
191
+ var BASE_URL = "https://api.comerge.ai";
192
192
 
193
193
  // src/core/services/http/public.ts
194
194
  var CLIENT_KEY_HEADER = "x-comerge-api-key";
195
- var clientApiKey = null;
195
+ var clientKey = null;
196
196
  var publicApi = axios.create({
197
197
  baseURL: BASE_URL,
198
198
  timeout: 3e4,
@@ -201,19 +201,19 @@ var publicApi = axios.create({
201
201
  "Content-Type": "application/json"
202
202
  }
203
203
  });
204
- function setClientApiKey(apiKey) {
204
+ function setClientKey(clientKeyInput) {
205
205
  var _a;
206
- const trimmed = ((_a = apiKey == null ? void 0 : apiKey.trim) == null ? void 0 : _a.call(apiKey)) ?? "";
206
+ const trimmed = ((_a = clientKeyInput == null ? void 0 : clientKeyInput.trim) == null ? void 0 : _a.call(clientKeyInput)) ?? "";
207
207
  if (!trimmed) {
208
- throw new Error("comerge-studio: apiKey is required");
208
+ throw new Error("comerge-studio: clientKey is required");
209
209
  }
210
- clientApiKey = trimmed;
210
+ clientKey = trimmed;
211
211
  publicApi.defaults.headers.common[CLIENT_KEY_HEADER] = trimmed;
212
212
  }
213
213
  publicApi.interceptors.request.use((config) => {
214
- if (!clientApiKey) return config;
214
+ if (!clientKey) return config;
215
215
  config.headers = config.headers ?? {};
216
- config.headers[CLIENT_KEY_HEADER] = clientApiKey;
216
+ config.headers[CLIENT_KEY_HEADER] = clientKey;
217
217
  return config;
218
218
  });
219
219
 
@@ -278,42 +278,9 @@ async function ensureAnonymousSession() {
278
278
  return { user: data.user, isNew: true };
279
279
  }
280
280
 
281
- // src/data/base-repository.ts
282
- var BaseRepository = class {
283
- unwrapOrThrow(res) {
284
- if (res.success && res.responseObject) return res.responseObject;
285
- const msg = res.message || "Request failed";
286
- throw new Error(msg);
287
- }
288
- };
289
-
290
- // src/data/base-remote.ts
291
- var BaseRemote = class {
292
- };
293
-
294
- // src/data/public/studio-config/remote.ts
295
- var StudioConfigRemoteDataSourceImpl = class extends BaseRemote {
296
- async get() {
297
- const { data } = await publicApi.get("/v1/public/studio-config");
298
- return data;
299
- }
300
- };
301
- var studioConfigRemoteDataSource = new StudioConfigRemoteDataSourceImpl();
302
-
303
- // src/data/public/studio-config/repository.ts
304
- var StudioConfigRepositoryImpl = class extends BaseRepository {
305
- constructor(remote) {
306
- super();
307
- this.remote = remote;
308
- }
309
- async get() {
310
- const res = await this.remote.get();
311
- return this.unwrapOrThrow(res);
312
- }
313
- };
314
- var studioConfigRepository = new StudioConfigRepositoryImpl(studioConfigRemoteDataSource);
315
-
316
281
  // src/studio/bootstrap/useStudioBootstrap.ts
282
+ var SUPABASE_URL = "https://xtfxwbckjpfmqubnsusu.supabase.co";
283
+ var SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh0Znh3YmNranBmbXF1Ym5zdXN1Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjA2MDEyMzAsImV4cCI6MjA3NjE3NzIzMH0.dzWGAWrK4CvrmHVHzf8w7JlUZohdap0ZPnLZnABMV8s";
317
284
  function useStudioBootstrap(options) {
318
285
  const [state, setState] = React.useState({
319
286
  ready: false,
@@ -324,11 +291,10 @@ function useStudioBootstrap(options) {
324
291
  let cancelled = false;
325
292
  (async () => {
326
293
  try {
327
- setClientApiKey(options.apiKey);
294
+ setClientKey(options.clientKey);
328
295
  const requireAuth = isSupabaseClientInjected();
329
296
  if (!requireAuth) {
330
- const cfg = await studioConfigRepository.get();
331
- setSupabaseConfig(cfg);
297
+ setSupabaseConfig({ url: SUPABASE_URL, anonKey: SUPABASE_ANON_KEY });
332
298
  }
333
299
  const { user } = requireAuth ? await ensureAuthenticatedSession() : await ensureAnonymousSession();
334
300
  if (cancelled) return;
@@ -342,14 +308,14 @@ function useStudioBootstrap(options) {
342
308
  return () => {
343
309
  cancelled = true;
344
310
  };
345
- }, [options.apiKey]);
311
+ }, [options.clientKey]);
346
312
  return state;
347
313
  }
348
314
 
349
315
  // src/studio/bootstrap/StudioBootstrap.tsx
350
316
  import { Fragment, jsx as jsx2 } from "react/jsx-runtime";
351
- function StudioBootstrap({ children, fallback, renderError, apiKey }) {
352
- const { ready, error, userId } = useStudioBootstrap({ apiKey });
317
+ function StudioBootstrap({ children, fallback, renderError, clientKey: clientKey2 }) {
318
+ const { ready, error, userId } = useStudioBootstrap({ clientKey: clientKey2 });
353
319
  if (error) {
354
320
  return /* @__PURE__ */ jsx2(View, { style: { flex: 1, justifyContent: "center", alignItems: "center", padding: 24 }, children: renderError ? renderError(error) : /* @__PURE__ */ jsx2(Text, { variant: "bodyMuted", children: error.message }) });
355
321
  }
@@ -497,6 +463,10 @@ var createApiClient = (baseURL) => {
497
463
  };
498
464
  var api = createApiClient(BASE_URL);
499
465
 
466
+ // src/data/base-remote.ts
467
+ var BaseRemote = class {
468
+ };
469
+
500
470
  // src/data/apps/remote.ts
501
471
  var AppsRemoteDataSourceImpl = class extends BaseRemote {
502
472
  async list(projectId) {
@@ -552,6 +522,15 @@ var AppsRemoteDataSourceImpl = class extends BaseRemote {
552
522
  };
553
523
  var appsRemoteDataSource = new AppsRemoteDataSourceImpl();
554
524
 
525
+ // src/data/base-repository.ts
526
+ var BaseRepository = class {
527
+ unwrapOrThrow(res) {
528
+ if (res.success && res.responseObject) return res.responseObject;
529
+ const msg = res.message || "Request failed";
530
+ throw new Error(msg);
531
+ }
532
+ };
533
+
555
534
  // src/data/apps/repository.ts
556
535
  function mapDbAppRow(row) {
557
536
  return {
@@ -904,6 +883,7 @@ function useThreadMessages(threadId) {
904
883
  // src/studio/hooks/useBundleManager.ts
905
884
  import * as React5 from "react";
906
885
  import * as FileSystem from "expo-file-system/legacy";
886
+ import { Asset } from "expo-asset";
907
887
 
908
888
  // src/data/apps/bundles/remote.ts
909
889
  var BundlesRemoteDataSourceImpl = class extends BaseRemote {
@@ -1062,6 +1042,24 @@ async function deleteFileIfExists(fileUri) {
1062
1042
  } catch {
1063
1043
  }
1064
1044
  }
1045
+ async function hydrateBaseFromEmbeddedAsset(appId, platform, embedded) {
1046
+ if (!(embedded == null ? void 0 : embedded.module)) return null;
1047
+ const key = baseBundleKey(appId, platform);
1048
+ const targetUri = toBundleFileUri(key);
1049
+ const existing = await getExistingNonEmptyFileUri(targetUri);
1050
+ if (existing) return { bundlePath: existing, meta: embedded.meta ?? null };
1051
+ const asset = Asset.fromModule(embedded.module);
1052
+ await asset.downloadAsync();
1053
+ const sourceUri = asset.localUri ?? asset.uri;
1054
+ if (!sourceUri) return null;
1055
+ const info = await FileSystem.getInfoAsync(sourceUri);
1056
+ if (!info.exists) return null;
1057
+ await deleteFileIfExists(targetUri);
1058
+ await FileSystem.copyAsync({ from: sourceUri, to: targetUri });
1059
+ const finalUri = await getExistingNonEmptyFileUri(targetUri);
1060
+ if (!finalUri) return null;
1061
+ return { bundlePath: finalUri, meta: embedded.meta ?? null };
1062
+ }
1065
1063
  async function safeReplaceFileFromUrl(url, targetUri, tmpKey) {
1066
1064
  const tmpUri = toBundleFileUri(`tmp:${tmpKey}:${Date.now()}`);
1067
1065
  try {
@@ -1134,7 +1132,8 @@ async function resolveBundlePath(src, platform, mode) {
1134
1132
  function useBundleManager({
1135
1133
  base,
1136
1134
  platform,
1137
- canRequestLatest = true
1135
+ canRequestLatest = true,
1136
+ embeddedBaseBundles
1138
1137
  }) {
1139
1138
  const [bundlePath, setBundlePath] = React5.useState(null);
1140
1139
  const [renderToken, setRenderToken] = React5.useState(0);
@@ -1145,6 +1144,8 @@ function useBundleManager({
1145
1144
  const [isTesting, setIsTesting] = React5.useState(false);
1146
1145
  const baseRef = React5.useRef(base);
1147
1146
  baseRef.current = base;
1147
+ const embeddedBaseBundlesRef = React5.useRef(embeddedBaseBundles);
1148
+ embeddedBaseBundlesRef.current = embeddedBaseBundles;
1148
1149
  const baseOpIdRef = React5.useRef(0);
1149
1150
  const testOpIdRef = React5.useRef(0);
1150
1151
  const activeLoadModeRef = React5.useRef(null);
@@ -1167,16 +1168,29 @@ function useBundleManager({
1167
1168
  const hasCompletedFirstNetworkBaseLoadRef = React5.useRef(false);
1168
1169
  const hydrateBaseFromDisk = React5.useCallback(
1169
1170
  async (appId, reason) => {
1171
+ var _a;
1170
1172
  try {
1171
1173
  const dir = bundlesCacheDir();
1172
1174
  await ensureDir(dir);
1173
1175
  const key = baseBundleKey(appId, platform);
1174
1176
  const uri = toBundleFileUri(key);
1175
- const existing = await getExistingNonEmptyFileUri(uri);
1177
+ let existing = await getExistingNonEmptyFileUri(uri);
1178
+ let embeddedMeta = null;
1179
+ if (!existing) {
1180
+ const embedded = (_a = embeddedBaseBundlesRef.current) == null ? void 0 : _a[platform];
1181
+ const hydrated = await hydrateBaseFromEmbeddedAsset(appId, platform, embedded);
1182
+ if (hydrated == null ? void 0 : hydrated.bundlePath) {
1183
+ existing = hydrated.bundlePath;
1184
+ embeddedMeta = hydrated.meta ?? null;
1185
+ if (embeddedMeta) {
1186
+ await writeJsonFile(toBundleMetaFileUri(key), embeddedMeta);
1187
+ }
1188
+ }
1189
+ }
1176
1190
  if (existing) {
1177
1191
  lastBaseBundlePathRef.current = existing;
1178
1192
  setBundlePath(existing);
1179
- const meta = await readJsonFile(toBundleMetaFileUri(key));
1193
+ const meta = embeddedMeta ?? await readJsonFile(toBundleMetaFileUri(key));
1180
1194
  if (meta == null ? void 0 : meta.fingerprint) {
1181
1195
  lastBaseFingerprintRef.current = meta.fingerprint;
1182
1196
  }
@@ -1763,11 +1777,28 @@ function hasNoOutcomeAfterLastHuman(messages) {
1763
1777
  }
1764
1778
 
1765
1779
  // src/studio/ui/RuntimeRenderer.tsx
1780
+ import * as React9 from "react";
1766
1781
  import { View as View2 } from "react-native";
1767
1782
  import { ComergeRuntimeRenderer } from "@comergehq/runtime";
1768
1783
  import { jsx as jsx3 } from "react/jsx-runtime";
1769
- function RuntimeRenderer({ appKey, bundlePath, forcePreparing, renderToken, style }) {
1784
+ function RuntimeRenderer({
1785
+ appKey,
1786
+ bundlePath,
1787
+ forcePreparing,
1788
+ renderToken,
1789
+ style,
1790
+ allowInitialPreparing = true
1791
+ }) {
1792
+ const [hasRenderedOnce, setHasRenderedOnce] = React9.useState(false);
1793
+ React9.useEffect(() => {
1794
+ if (bundlePath) {
1795
+ setHasRenderedOnce(true);
1796
+ }
1797
+ }, [bundlePath]);
1770
1798
  if (!bundlePath || forcePreparing) {
1799
+ if (!hasRenderedOnce && !forcePreparing && !allowInitialPreparing) {
1800
+ return /* @__PURE__ */ jsx3(View2, { style: [{ flex: 1 }, style] });
1801
+ }
1771
1802
  return /* @__PURE__ */ jsx3(View2, { style: [{ flex: 1, justifyContent: "center", alignItems: "center", padding: 24 }, style], children: /* @__PURE__ */ jsx3(Text, { variant: "bodyMuted", children: "Preparing app\u2026" }) });
1772
1803
  }
1773
1804
  return /* @__PURE__ */ jsx3(View2, { style: [{ flex: 1 }, style], children: /* @__PURE__ */ jsx3(
@@ -1782,11 +1813,11 @@ function RuntimeRenderer({ appKey, bundlePath, forcePreparing, renderToken, styl
1782
1813
  }
1783
1814
 
1784
1815
  // src/studio/ui/StudioOverlay.tsx
1785
- import * as React41 from "react";
1816
+ import * as React42 from "react";
1786
1817
  import { Keyboard as Keyboard5, Platform as Platform10, View as View44, useWindowDimensions as useWindowDimensions4 } from "react-native";
1787
1818
 
1788
1819
  // src/components/studio-sheet/StudioBottomSheet.tsx
1789
- import * as React11 from "react";
1820
+ import * as React12 from "react";
1790
1821
  import { AppState as AppState3, Keyboard, View as View4 } from "react-native";
1791
1822
  import BottomSheet from "@gorhom/bottom-sheet";
1792
1823
  import { useSafeAreaInsets } from "react-native-safe-area-context";
@@ -1796,20 +1827,20 @@ import { Platform as Platform3, View as View3 } from "react-native";
1796
1827
  import { isLiquidGlassSupported } from "@callstack/liquid-glass";
1797
1828
 
1798
1829
  // src/components/utils/ResettableLiquidGlassView.tsx
1799
- import * as React10 from "react";
1830
+ import * as React11 from "react";
1800
1831
  import { LiquidGlassView } from "@callstack/liquid-glass";
1801
1832
 
1802
1833
  // src/components/utils/liquidGlassReset.tsx
1803
- import * as React9 from "react";
1834
+ import * as React10 from "react";
1804
1835
  import { AppState as AppState2, Platform as Platform2 } from "react-native";
1805
1836
  import { jsx as jsx4 } from "react/jsx-runtime";
1806
- var LiquidGlassResetContext = React9.createContext(0);
1837
+ var LiquidGlassResetContext = React10.createContext(0);
1807
1838
  function LiquidGlassResetProvider({
1808
1839
  children,
1809
1840
  resetTriggers = []
1810
1841
  }) {
1811
- const [token, setToken] = React9.useState(0);
1812
- React9.useEffect(() => {
1842
+ const [token, setToken] = React10.useState(0);
1843
+ React10.useEffect(() => {
1813
1844
  if (Platform2.OS !== "ios") return;
1814
1845
  const onChange = (state) => {
1815
1846
  if (state === "active") setToken((t) => t + 1);
@@ -1817,21 +1848,21 @@ function LiquidGlassResetProvider({
1817
1848
  const sub = AppState2.addEventListener("change", onChange);
1818
1849
  return () => sub.remove();
1819
1850
  }, []);
1820
- React9.useEffect(() => {
1851
+ React10.useEffect(() => {
1821
1852
  setToken((t) => t + 1);
1822
1853
  }, resetTriggers);
1823
1854
  return /* @__PURE__ */ jsx4(LiquidGlassResetContext.Provider, { value: token, children });
1824
1855
  }
1825
1856
  function useLiquidGlassResetToken() {
1826
- return React9.useContext(LiquidGlassResetContext);
1857
+ return React10.useContext(LiquidGlassResetContext);
1827
1858
  }
1828
1859
 
1829
1860
  // src/components/utils/ResettableLiquidGlassView.tsx
1830
1861
  import { jsx as jsx5 } from "react/jsx-runtime";
1831
1862
  function ResettableLiquidGlassView({ children, ...props }) {
1832
1863
  const token = useLiquidGlassResetToken();
1833
- const [layoutBootKey, setLayoutBootKey] = React10.useState(0);
1834
- const sawNonZeroLayoutRef = React10.useRef(false);
1864
+ const [layoutBootKey, setLayoutBootKey] = React11.useState(0);
1865
+ const sawNonZeroLayoutRef = React11.useRef(false);
1835
1866
  const onLayout = (e) => {
1836
1867
  var _a;
1837
1868
  (_a = props.onLayout) == null ? void 0 : _a.call(props, e);
@@ -1905,11 +1936,11 @@ function StudioBottomSheet({
1905
1936
  }) {
1906
1937
  const theme = useTheme();
1907
1938
  const insets = useSafeAreaInsets();
1908
- const internalSheetRef = React11.useRef(null);
1939
+ const internalSheetRef = React12.useRef(null);
1909
1940
  const resolvedSheetRef = sheetRef ?? internalSheetRef;
1910
- const currentIndexRef = React11.useRef(open ? snapPoints.length - 1 : -1);
1911
- const lastAppStateRef = React11.useRef(AppState3.currentState);
1912
- React11.useEffect(() => {
1941
+ const currentIndexRef = React12.useRef(open ? snapPoints.length - 1 : -1);
1942
+ const lastAppStateRef = React12.useRef(AppState3.currentState);
1943
+ React12.useEffect(() => {
1913
1944
  const sub = AppState3.addEventListener("change", (state) => {
1914
1945
  const prev = lastAppStateRef.current;
1915
1946
  lastAppStateRef.current = state;
@@ -1929,7 +1960,7 @@ function StudioBottomSheet({
1929
1960
  });
1930
1961
  return () => sub.remove();
1931
1962
  }, [open, resolvedSheetRef]);
1932
- React11.useEffect(() => {
1963
+ React12.useEffect(() => {
1933
1964
  const sheet = resolvedSheetRef.current;
1934
1965
  if (!sheet) return;
1935
1966
  if (open) {
@@ -1938,7 +1969,7 @@ function StudioBottomSheet({
1938
1969
  sheet.close();
1939
1970
  }
1940
1971
  }, [open, resolvedSheetRef, snapPoints.length]);
1941
- const handleChange = React11.useCallback(
1972
+ const handleChange = React12.useCallback(
1942
1973
  (index) => {
1943
1974
  currentIndexRef.current = index;
1944
1975
  onOpenChange == null ? void 0 : onOpenChange(index >= 0);
@@ -1967,12 +1998,12 @@ function StudioBottomSheet({
1967
1998
  }
1968
1999
 
1969
2000
  // src/components/studio-sheet/StudioSheetPager.tsx
1970
- import * as React12 from "react";
2001
+ import * as React13 from "react";
1971
2002
  import { Animated } from "react-native";
1972
2003
  import { jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
1973
2004
  function StudioSheetPager({ activePage, width, preview, chat, style }) {
1974
- const anim = React12.useRef(new Animated.Value(activePage === "chat" ? 1 : 0)).current;
1975
- React12.useEffect(() => {
2005
+ const anim = React13.useRef(new Animated.Value(activePage === "chat" ? 1 : 0)).current;
2006
+ React13.useEffect(() => {
1976
2007
  Animated.spring(anim, {
1977
2008
  toValue: activePage === "chat" ? 1 : 0,
1978
2009
  useNativeDriver: true,
@@ -2020,8 +2051,8 @@ function StudioSheetPager({ activePage, width, preview, chat, style }) {
2020
2051
  ] });
2021
2052
  }
2022
2053
 
2023
- // src/components/floating-draggable-button/FloatingDraggableButton.tsx
2024
- import { useCallback as useCallback8, useEffect as useEffect10, useMemo as useMemo3, useRef as useRef7 } from "react";
2054
+ // src/components/bubble/Bubble.tsx
2055
+ import { useCallback as useCallback8, useEffect as useEffect11, useMemo as useMemo3, useRef as useRef7 } from "react";
2025
2056
  import {
2026
2057
  PanResponder,
2027
2058
  Pressable,
@@ -2044,7 +2075,7 @@ import Animated2, {
2044
2075
  } from "react-native-reanimated";
2045
2076
  import { isLiquidGlassSupported as isLiquidGlassSupported2 } from "@callstack/liquid-glass";
2046
2077
 
2047
- // src/components/floating-draggable-button/constants.ts
2078
+ // src/components/bubble/constants.ts
2048
2079
  var DEFAULT_SIZE = 48;
2049
2080
  var DEFAULT_EDGE_PADDING = 10;
2050
2081
  var DEFAULT_OFFSET = {
@@ -2055,7 +2086,7 @@ var ENTER_SCALE_FROM = 0.3;
2055
2086
  var ENTER_ROTATION_FROM_DEG = -180;
2056
2087
  var PULSE_DURATION_MS = 900;
2057
2088
 
2058
- // src/components/floating-draggable-button/FloatingDraggableButton.tsx
2089
+ // src/components/bubble/Bubble.tsx
2059
2090
  import { jsx as jsx9, jsxs as jsxs3 } from "react/jsx-runtime";
2060
2091
  var HIDDEN_OFFSET_X = 20;
2061
2092
  var SPRING_POSITION = { damping: 12, stiffness: 100, mass: 0.8 };
@@ -2077,7 +2108,7 @@ function getHiddenTranslateY(height) {
2077
2108
  function getFinalTranslateY(height, size, bottomOffset) {
2078
2109
  return height - size - bottomOffset;
2079
2110
  }
2080
- function FloatingDraggableButton({
2111
+ function Bubble({
2081
2112
  onPress,
2082
2113
  size = DEFAULT_SIZE,
2083
2114
  disabled = false,
@@ -2098,7 +2129,7 @@ function FloatingDraggableButton({
2098
2129
  const { width, height } = useWindowDimensions();
2099
2130
  const isDanger = variant === "danger";
2100
2131
  const onPressRef = useRef7(onPress);
2101
- useEffect10(() => {
2132
+ useEffect11(() => {
2102
2133
  onPressRef.current = onPress;
2103
2134
  }, [onPress]);
2104
2135
  const fallbackBgColor = useMemo3(() => {
@@ -2139,7 +2170,7 @@ function FloatingDraggableButton({
2139
2170
  }
2140
2171
  });
2141
2172
  }, [animateToHidden]);
2142
- useEffect10(() => {
2173
+ useEffect11(() => {
2143
2174
  if (isLoading) {
2144
2175
  borderPulse.value = withRepeat(
2145
2176
  withSequence(
@@ -2164,7 +2195,7 @@ function FloatingDraggableButton({
2164
2195
  );
2165
2196
  rotation.value = withSpring(0, SPRING_ROTATION_IN);
2166
2197
  }, [height, offset.bottom, offset.left, rotation, scale, size, translateX, translateY]);
2167
- useEffect10(() => {
2198
+ useEffect11(() => {
2168
2199
  const timer = setTimeout(() => {
2169
2200
  if (visible) {
2170
2201
  animateIn();
@@ -2172,7 +2203,7 @@ function FloatingDraggableButton({
2172
2203
  }, 100);
2173
2204
  return () => clearTimeout(timer);
2174
2205
  }, []);
2175
- useEffect10(() => {
2206
+ useEffect11(() => {
2176
2207
  if (visible && isAnimatingOut.current) {
2177
2208
  animateIn();
2178
2209
  } else if (!visible && !isAnimatingOut.current) {
@@ -2180,7 +2211,7 @@ function FloatingDraggableButton({
2180
2211
  isAnimatingOut.current = true;
2181
2212
  }
2182
2213
  }, [visible, animateIn, animateToHidden]);
2183
- useEffect10(() => {
2214
+ useEffect11(() => {
2184
2215
  if (forceShowTrigger > 0 && visible) {
2185
2216
  isAnimatingOut.current = false;
2186
2217
  animateIn();
@@ -2302,7 +2333,7 @@ var styles = StyleSheet.create({
2302
2333
  });
2303
2334
 
2304
2335
  // src/components/overlays/EdgeGlowFrame.tsx
2305
- import * as React13 from "react";
2336
+ import * as React14 from "react";
2306
2337
  import { Animated as Animated3, View as View6 } from "react-native";
2307
2338
  import { LinearGradient } from "expo-linear-gradient";
2308
2339
 
@@ -2345,8 +2376,8 @@ function EdgeGlowFrame({
2345
2376
  }) {
2346
2377
  const theme = useTheme();
2347
2378
  const alpha = Math.max(0, Math.min(1, intensity));
2348
- const anim = React13.useRef(new Animated3.Value(visible ? 1 : 0)).current;
2349
- React13.useEffect(() => {
2379
+ const anim = React14.useRef(new Animated3.Value(visible ? 1 : 0)).current;
2380
+ React14.useEffect(() => {
2350
2381
  Animated3.timing(anim, {
2351
2382
  toValue: visible ? 1 : 0,
2352
2383
  duration: 300,
@@ -2397,12 +2428,12 @@ function EdgeGlowFrame({
2397
2428
  }
2398
2429
 
2399
2430
  // src/components/draw/DrawModeOverlay.tsx
2400
- import * as React16 from "react";
2431
+ import * as React17 from "react";
2401
2432
  import { StyleSheet as StyleSheet3, View as View10 } from "react-native";
2402
2433
  import { captureRef } from "react-native-view-shot";
2403
2434
 
2404
2435
  // src/components/draw/DrawSurface.tsx
2405
- import * as React14 from "react";
2436
+ import * as React15 from "react";
2406
2437
  import { PanResponder as PanResponder2, StyleSheet as StyleSheet2, View as View7 } from "react-native";
2407
2438
  import Svg, { Path } from "react-native-svg";
2408
2439
 
@@ -2434,25 +2465,25 @@ function DrawSurface({
2434
2465
  style,
2435
2466
  minDistance = 1
2436
2467
  }) {
2437
- const [renderTick, setRenderTick] = React14.useState(0);
2438
- const currentPointsRef = React14.useRef([]);
2439
- const rafRef = React14.useRef(null);
2440
- const triggerRender = React14.useCallback(() => {
2468
+ const [renderTick, setRenderTick] = React15.useState(0);
2469
+ const currentPointsRef = React15.useRef([]);
2470
+ const rafRef = React15.useRef(null);
2471
+ const triggerRender = React15.useCallback(() => {
2441
2472
  if (rafRef.current !== null) return;
2442
2473
  rafRef.current = requestAnimationFrame(() => {
2443
2474
  rafRef.current = null;
2444
2475
  setRenderTick((n) => n + 1);
2445
2476
  });
2446
2477
  }, []);
2447
- React14.useEffect(() => () => {
2478
+ React15.useEffect(() => () => {
2448
2479
  if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
2449
2480
  }, []);
2450
- const onStart = React14.useCallback((e) => {
2481
+ const onStart = React15.useCallback((e) => {
2451
2482
  const { locationX, locationY } = e.nativeEvent;
2452
2483
  currentPointsRef.current = [{ x: locationX, y: locationY }];
2453
2484
  triggerRender();
2454
2485
  }, [triggerRender]);
2455
- const onMove = React14.useCallback((e, _g) => {
2486
+ const onMove = React15.useCallback((e, _g) => {
2456
2487
  const { locationX, locationY } = e.nativeEvent;
2457
2488
  const pts = currentPointsRef.current;
2458
2489
  if (pts.length > 0) {
@@ -2465,7 +2496,7 @@ function DrawSurface({
2465
2496
  currentPointsRef.current = [...pts, { x: locationX, y: locationY }];
2466
2497
  triggerRender();
2467
2498
  }, [minDistance, triggerRender]);
2468
- const onEnd = React14.useCallback(() => {
2499
+ const onEnd = React15.useCallback(() => {
2469
2500
  const points = currentPointsRef.current;
2470
2501
  if (points.length > 0) {
2471
2502
  onAddStroke({ points, color, width: strokeWidth });
@@ -2473,7 +2504,7 @@ function DrawSurface({
2473
2504
  currentPointsRef.current = [];
2474
2505
  triggerRender();
2475
2506
  }, [color, onAddStroke, strokeWidth, triggerRender]);
2476
- const panResponder = React14.useMemo(
2507
+ const panResponder = React15.useMemo(
2477
2508
  () => PanResponder2.create({
2478
2509
  onStartShouldSetPanResponder: () => true,
2479
2510
  onMoveShouldSetPanResponder: () => true,
@@ -2523,7 +2554,7 @@ var styles2 = StyleSheet2.create({
2523
2554
  });
2524
2555
 
2525
2556
  // src/components/draw/DrawToolbar.tsx
2526
- import * as React15 from "react";
2557
+ import * as React16 from "react";
2527
2558
  import {
2528
2559
  ActivityIndicator,
2529
2560
  Animated as Animated4,
@@ -2620,11 +2651,11 @@ function DrawToolbar({
2620
2651
  }) {
2621
2652
  const insets = useSafeAreaInsets2();
2622
2653
  const { width: screenWidth, height: screenHeight } = useWindowDimensions2();
2623
- const [expanded, setExpanded] = React15.useState(false);
2624
- const pos = React15.useRef(new Animated4.ValueXY({ x: screenWidth / 2 - 110, y: -140 })).current;
2625
- const start = React15.useRef({ x: 0, y: 0 });
2626
- const currentPos = React15.useRef({ x: 0, y: 0 });
2627
- React15.useEffect(() => {
2654
+ const [expanded, setExpanded] = React16.useState(false);
2655
+ const pos = React16.useRef(new Animated4.ValueXY({ x: screenWidth / 2 - 110, y: -140 })).current;
2656
+ const start = React16.useRef({ x: 0, y: 0 });
2657
+ const currentPos = React16.useRef({ x: 0, y: 0 });
2658
+ React16.useEffect(() => {
2628
2659
  if (hidden) return;
2629
2660
  Animated4.spring(pos.y, {
2630
2661
  toValue: insets.top + 60,
@@ -2634,7 +2665,7 @@ function DrawToolbar({
2634
2665
  mass: 0.8
2635
2666
  }).start();
2636
2667
  }, [hidden, insets.top, pos.y]);
2637
- React15.useEffect(() => {
2668
+ React16.useEffect(() => {
2638
2669
  const id = pos.addListener((v) => {
2639
2670
  currentPos.current = { x: v.x ?? 0, y: v.y ?? 0 };
2640
2671
  });
@@ -2642,7 +2673,7 @@ function DrawToolbar({
2642
2673
  pos.removeListener(id);
2643
2674
  };
2644
2675
  }, [pos]);
2645
- const clamp2 = React15.useCallback(
2676
+ const clamp2 = React16.useCallback(
2646
2677
  (x, y) => {
2647
2678
  const minX = 10;
2648
2679
  const maxX = Math.max(10, screenWidth - 230);
@@ -2652,7 +2683,7 @@ function DrawToolbar({
2652
2683
  },
2653
2684
  [insets.top, screenHeight, screenWidth]
2654
2685
  );
2655
- const panResponder = React15.useMemo(
2686
+ const panResponder = React16.useMemo(
2656
2687
  () => PanResponder3.create({
2657
2688
  onStartShouldSetPanResponder: () => false,
2658
2689
  onMoveShouldSetPanResponder: (_e, g) => Math.abs(g.dx) > 5 || Math.abs(g.dy) > 5,
@@ -2680,7 +2711,7 @@ function DrawToolbar({
2680
2711
  children
2681
2712
  }) {
2682
2713
  const isDisabled = Boolean(disabled) || Boolean(capturingDisabled);
2683
- const [pressed, setPressed] = React15.useState(false);
2714
+ const [pressed, setPressed] = React16.useState(false);
2684
2715
  return /* @__PURE__ */ jsx13(
2685
2716
  View9,
2686
2717
  {
@@ -2818,7 +2849,7 @@ function DrawModeOverlay({
2818
2849
  renderDragHandle
2819
2850
  }) {
2820
2851
  const theme = useTheme();
2821
- const defaultPalette = React16.useMemo(
2852
+ const defaultPalette = React17.useMemo(
2822
2853
  () => [
2823
2854
  "#EF4444",
2824
2855
  // Red
@@ -2836,11 +2867,11 @@ function DrawModeOverlay({
2836
2867
  []
2837
2868
  );
2838
2869
  const colors = palette && palette.length > 0 ? palette : defaultPalette;
2839
- const [selectedColor, setSelectedColor] = React16.useState(colors[0] ?? "#EF4444");
2840
- const [strokes, setStrokes] = React16.useState([]);
2841
- const [capturing, setCapturing] = React16.useState(false);
2842
- const [hideUi, setHideUi] = React16.useState(false);
2843
- React16.useEffect(() => {
2870
+ const [selectedColor, setSelectedColor] = React17.useState(colors[0] ?? "#EF4444");
2871
+ const [strokes, setStrokes] = React17.useState([]);
2872
+ const [capturing, setCapturing] = React17.useState(false);
2873
+ const [hideUi, setHideUi] = React17.useState(false);
2874
+ React17.useEffect(() => {
2844
2875
  if (!visible) return;
2845
2876
  setStrokes([]);
2846
2877
  setSelectedColor(colors[0] ?? "#EF4444");
@@ -2848,14 +2879,14 @@ function DrawModeOverlay({
2848
2879
  setHideUi(false);
2849
2880
  }, [colors, visible]);
2850
2881
  const canUndo = strokes.length > 0;
2851
- const handleUndo = React16.useCallback(() => {
2882
+ const handleUndo = React17.useCallback(() => {
2852
2883
  setStrokes((prev) => prev.slice(0, -1));
2853
2884
  }, []);
2854
- const handleCancel = React16.useCallback(() => {
2885
+ const handleCancel = React17.useCallback(() => {
2855
2886
  setStrokes([]);
2856
2887
  onCancel();
2857
2888
  }, [onCancel]);
2858
- const handleDone = React16.useCallback(async () => {
2889
+ const handleDone = React17.useCallback(async () => {
2859
2890
  if (!captureTargetRef.current || capturing) return;
2860
2891
  try {
2861
2892
  setCapturing(true);
@@ -2915,7 +2946,7 @@ var styles3 = StyleSheet3.create({
2915
2946
  });
2916
2947
 
2917
2948
  // src/components/comments/AppCommentsSheet.tsx
2918
- import * as React23 from "react";
2949
+ import * as React24 from "react";
2919
2950
  import { ActivityIndicator as ActivityIndicator3, Keyboard as Keyboard3, Platform as Platform6, Pressable as Pressable5, View as View14 } from "react-native";
2920
2951
  import {
2921
2952
  BottomSheetBackdrop,
@@ -2927,7 +2958,7 @@ import { isLiquidGlassSupported as isLiquidGlassSupported4 } from "@callstack/li
2927
2958
  import { Play as Play2 } from "lucide-react-native";
2928
2959
 
2929
2960
  // src/components/chat/ChatComposer.tsx
2930
- import * as React18 from "react";
2961
+ import * as React19 from "react";
2931
2962
  import {
2932
2963
  ActivityIndicator as ActivityIndicator2,
2933
2964
  Animated as Animated5,
@@ -2941,11 +2972,11 @@ import { isLiquidGlassSupported as isLiquidGlassSupported3 } from "@callstack/li
2941
2972
  import { Plus } from "lucide-react-native";
2942
2973
 
2943
2974
  // src/components/chat/MultilineTextInput.tsx
2944
- import * as React17 from "react";
2975
+ import * as React18 from "react";
2945
2976
  import { TextInput } from "react-native";
2946
2977
  import { BottomSheetTextInput } from "@gorhom/bottom-sheet";
2947
2978
  import { jsx as jsx15 } from "react/jsx-runtime";
2948
- var MultilineTextInput = React17.forwardRef(function MultilineTextInput2({ useBottomSheetTextInput = false, placeholder, placeholderTextColor, style, ...props }, ref) {
2979
+ var MultilineTextInput = React18.forwardRef(function MultilineTextInput2({ useBottomSheetTextInput = false, placeholder, placeholderTextColor, style, ...props }, ref) {
2949
2980
  const theme = useTheme();
2950
2981
  const baseStyle = {
2951
2982
  minHeight: 44,
@@ -3029,7 +3060,7 @@ function AspectRatioThumbnail({
3029
3060
  onRemove,
3030
3061
  renderRemoveIcon
3031
3062
  }) {
3032
- const [aspectRatio, setAspectRatio] = React18.useState(1);
3063
+ const [aspectRatio, setAspectRatio] = React19.useState(1);
3033
3064
  return /* @__PURE__ */ jsxs8(View11, { style: { height: THUMBNAIL_HEIGHT, aspectRatio, position: "relative" }, children: [
3034
3065
  /* @__PURE__ */ jsx17(View11, { style: { flex: 1, borderRadius: 8, overflow: "hidden" }, children: /* @__PURE__ */ jsx17(
3035
3066
  Image,
@@ -3086,19 +3117,19 @@ function ChatComposer({
3086
3117
  style
3087
3118
  }) {
3088
3119
  const theme = useTheme();
3089
- const [internal, setInternal] = React18.useState("");
3120
+ const [internal, setInternal] = React19.useState("");
3090
3121
  const text = value ?? internal;
3091
3122
  const setText = onChangeValue ?? setInternal;
3092
3123
  const hasAttachments = attachments.length > 0;
3093
3124
  const hasText = text.trim().length > 0;
3094
3125
  const composerMinHeight = hasAttachments ? THUMBNAIL_HEIGHT + 44 + 24 : 44;
3095
3126
  const isButtonDisabled = sending || disabled || sendDisabled;
3096
- const maxInputHeight = React18.useMemo(() => Dimensions.get("window").height * 0.5, []);
3097
- const shakeAnim = React18.useRef(new Animated5.Value(0)).current;
3098
- const [sendPressed, setSendPressed] = React18.useState(false);
3099
- const inputRef = React18.useRef(null);
3100
- const prevAutoFocusRef = React18.useRef(false);
3101
- React18.useEffect(() => {
3127
+ const maxInputHeight = React19.useMemo(() => Dimensions.get("window").height * 0.5, []);
3128
+ const shakeAnim = React19.useRef(new Animated5.Value(0)).current;
3129
+ const [sendPressed, setSendPressed] = React19.useState(false);
3130
+ const inputRef = React19.useRef(null);
3131
+ const prevAutoFocusRef = React19.useRef(false);
3132
+ React19.useEffect(() => {
3102
3133
  const shouldFocus = autoFocus && !prevAutoFocusRef.current && !disabled && !sending;
3103
3134
  prevAutoFocusRef.current = autoFocus;
3104
3135
  if (!shouldFocus) return;
@@ -3108,7 +3139,7 @@ function ChatComposer({
3108
3139
  }, 75);
3109
3140
  return () => clearTimeout(t);
3110
3141
  }, [autoFocus, disabled, sending]);
3111
- const triggerShake = React18.useCallback(() => {
3142
+ const triggerShake = React19.useCallback(() => {
3112
3143
  shakeAnim.setValue(0);
3113
3144
  Animated5.sequence([
3114
3145
  Animated5.timing(shakeAnim, { toValue: 10, duration: 50, useNativeDriver: true }),
@@ -3118,7 +3149,7 @@ function ChatComposer({
3118
3149
  Animated5.timing(shakeAnim, { toValue: 0, duration: 50, useNativeDriver: true })
3119
3150
  ]).start();
3120
3151
  }, [shakeAnim]);
3121
- const handleSend = React18.useCallback(async () => {
3152
+ const handleSend = React19.useCallback(async () => {
3122
3153
  if (isButtonDisabled) return;
3123
3154
  if (!hasText) {
3124
3155
  triggerShake();
@@ -3251,7 +3282,7 @@ function ChatComposer({
3251
3282
  }
3252
3283
 
3253
3284
  // src/components/comments/CommentRow.tsx
3254
- import * as React19 from "react";
3285
+ import * as React20 from "react";
3255
3286
  import { View as View13 } from "react-native";
3256
3287
 
3257
3288
  // src/components/primitives/Avatar.tsx
@@ -3323,9 +3354,9 @@ function formatTimeAgo(iso) {
3323
3354
  import { jsx as jsx19, jsxs as jsxs9 } from "react/jsx-runtime";
3324
3355
  function CommentRow({ comment, showDivider }) {
3325
3356
  const theme = useTheme();
3326
- const [authorName, setAuthorName] = React19.useState(null);
3327
- const [authorAvatar, setAuthorAvatar] = React19.useState(null);
3328
- React19.useEffect(() => {
3357
+ const [authorName, setAuthorName] = React20.useState(null);
3358
+ const [authorAvatar, setAuthorAvatar] = React20.useState(null);
3359
+ React20.useEffect(() => {
3329
3360
  let cancelled = false;
3330
3361
  (async () => {
3331
3362
  try {
@@ -3365,7 +3396,7 @@ function CommentRow({ comment, showDivider }) {
3365
3396
  }
3366
3397
 
3367
3398
  // src/components/comments/useAppComments.ts
3368
- import * as React20 from "react";
3399
+ import * as React21 from "react";
3369
3400
 
3370
3401
  // src/data/comments/remote.ts
3371
3402
  var AppCommentsRemoteDataSourceImpl = class extends BaseRemote {
@@ -3437,18 +3468,18 @@ var appCommentsRepository = new AppCommentsRepositoryImpl(appCommentsRemoteDataS
3437
3468
 
3438
3469
  // src/components/comments/useAppComments.ts
3439
3470
  function useAppComments(appId) {
3440
- const [comments, setComments] = React20.useState([]);
3441
- const [loading, setLoading] = React20.useState(false);
3442
- const [sending, setSending] = React20.useState(false);
3443
- const [error, setError] = React20.useState(null);
3444
- const sortByCreatedAtAsc = React20.useCallback((items) => {
3471
+ const [comments, setComments] = React21.useState([]);
3472
+ const [loading, setLoading] = React21.useState(false);
3473
+ const [sending, setSending] = React21.useState(false);
3474
+ const [error, setError] = React21.useState(null);
3475
+ const sortByCreatedAtAsc = React21.useCallback((items) => {
3445
3476
  return [...items].sort((a, b) => {
3446
3477
  const at = a.createdAt ? new Date(a.createdAt).getTime() : 0;
3447
3478
  const bt = b.createdAt ? new Date(b.createdAt).getTime() : 0;
3448
3479
  return at - bt;
3449
3480
  });
3450
3481
  }, []);
3451
- const refresh = React20.useCallback(async () => {
3482
+ const refresh = React21.useCallback(async () => {
3452
3483
  if (!appId) {
3453
3484
  setComments([]);
3454
3485
  return;
@@ -3465,10 +3496,10 @@ function useAppComments(appId) {
3465
3496
  setLoading(false);
3466
3497
  }
3467
3498
  }, [appId, sortByCreatedAtAsc]);
3468
- React20.useEffect(() => {
3499
+ React21.useEffect(() => {
3469
3500
  void refresh();
3470
3501
  }, [refresh]);
3471
- const create = React20.useCallback(
3502
+ const create = React21.useCallback(
3472
3503
  async (text) => {
3473
3504
  if (!appId) return;
3474
3505
  const trimmed = text.trim();
@@ -3491,11 +3522,11 @@ function useAppComments(appId) {
3491
3522
  }
3492
3523
 
3493
3524
  // src/components/comments/useAppDetails.ts
3494
- import * as React21 from "react";
3525
+ import * as React22 from "react";
3495
3526
  function useAppDetails(appId) {
3496
- const [app, setApp] = React21.useState(null);
3497
- const [loading, setLoading] = React21.useState(false);
3498
- React21.useEffect(() => {
3527
+ const [app, setApp] = React22.useState(null);
3528
+ const [loading, setLoading] = React22.useState(false);
3529
+ React22.useEffect(() => {
3499
3530
  if (!appId) {
3500
3531
  setApp(null);
3501
3532
  return;
@@ -3520,11 +3551,11 @@ function useAppDetails(appId) {
3520
3551
  }
3521
3552
 
3522
3553
  // src/components/comments/useIosKeyboardSnapFix.ts
3523
- import * as React22 from "react";
3554
+ import * as React23 from "react";
3524
3555
  import { Keyboard as Keyboard2, Platform as Platform5 } from "react-native";
3525
3556
  function useIosKeyboardSnapFix(sheetRef, options) {
3526
- const [keyboardVisible, setKeyboardVisible] = React22.useState(false);
3527
- React22.useEffect(() => {
3557
+ const [keyboardVisible, setKeyboardVisible] = React23.useState(false);
3558
+ React23.useEffect(() => {
3528
3559
  if (Platform5.OS !== "ios") return;
3529
3560
  const show = Keyboard2.addListener("keyboardWillShow", () => setKeyboardVisible(true));
3530
3561
  const hide = Keyboard2.addListener("keyboardWillHide", () => {
@@ -3552,16 +3583,16 @@ import { jsx as jsx20, jsxs as jsxs10 } from "react/jsx-runtime";
3552
3583
  function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3553
3584
  const theme = useTheme();
3554
3585
  const insets = useSafeAreaInsets3();
3555
- const sheetRef = React23.useRef(null);
3556
- const snapPoints = React23.useMemo(() => ["50%", "90%"], []);
3557
- const currentIndexRef = React23.useRef(1);
3586
+ const sheetRef = React24.useRef(null);
3587
+ const snapPoints = React24.useMemo(() => ["50%", "90%"], []);
3588
+ const currentIndexRef = React24.useRef(1);
3558
3589
  const { comments, loading, sending, error, create, refresh } = useAppComments(appId);
3559
3590
  const { app, loading: loadingApp } = useAppDetails(appId);
3560
3591
  const { keyboardVisible } = useIosKeyboardSnapFix(sheetRef, {
3561
3592
  getCurrentIndex: () => currentIndexRef.current,
3562
3593
  targetIndex: 1
3563
3594
  });
3564
- React23.useEffect(() => {
3595
+ React24.useEffect(() => {
3565
3596
  var _a, _b;
3566
3597
  if (appId) {
3567
3598
  (_a = sheetRef.current) == null ? void 0 : _a.present();
@@ -3570,22 +3601,22 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3570
3601
  (_b = sheetRef.current) == null ? void 0 : _b.dismiss();
3571
3602
  }
3572
3603
  }, [appId, refresh]);
3573
- React23.useEffect(() => {
3604
+ React24.useEffect(() => {
3574
3605
  if (!appId) return;
3575
3606
  onCountChange == null ? void 0 : onCountChange(comments.length);
3576
3607
  }, [appId, comments.length, onCountChange]);
3577
- const renderBackdrop = React23.useCallback(
3608
+ const renderBackdrop = React24.useCallback(
3578
3609
  (props) => /* @__PURE__ */ jsx20(BottomSheetBackdrop, { ...props, disappearsOnIndex: -1, appearsOnIndex: 0, opacity: 0.5 }),
3579
3610
  []
3580
3611
  );
3581
- const handleChange = React23.useCallback(
3612
+ const handleChange = React24.useCallback(
3582
3613
  (index) => {
3583
3614
  currentIndexRef.current = index;
3584
3615
  if (index === -1) onClose();
3585
3616
  },
3586
3617
  [onClose]
3587
3618
  );
3588
- const handlePlay = React23.useCallback(async () => {
3619
+ const handlePlay = React24.useCallback(async () => {
3589
3620
  var _a;
3590
3621
  if (!appId) return;
3591
3622
  (_a = sheetRef.current) == null ? void 0 : _a.dismiss();
@@ -3795,7 +3826,7 @@ function StudioSheetHeader({ left, center, right, style }) {
3795
3826
  }
3796
3827
 
3797
3828
  // src/components/studio-sheet/StudioSheetHeaderIconButton.tsx
3798
- import * as React24 from "react";
3829
+ import * as React25 from "react";
3799
3830
  import { Pressable as Pressable6, View as View17 } from "react-native";
3800
3831
  import { isLiquidGlassSupported as isLiquidGlassSupported5 } from "@callstack/liquid-glass";
3801
3832
  import { jsx as jsx23 } from "react/jsx-runtime";
@@ -3810,7 +3841,7 @@ function StudioSheetHeaderIconButton({
3810
3841
  }) {
3811
3842
  const theme = useTheme();
3812
3843
  const size = 44;
3813
- const [pressed, setPressed] = React24.useState(false);
3844
+ const [pressed, setPressed] = React25.useState(false);
3814
3845
  const solidBg = intent === "danger" ? theme.colors.danger : intent === "primary" ? theme.colors.primary : theme.colors.neutral;
3815
3846
  const glassFallbackBg = theme.scheme === "dark" ? "#18181B" : "#F6F6F6";
3816
3847
  const glassInnerBg = intent === "danger" ? theme.colors.danger : theme.colors.primary;
@@ -4000,14 +4031,14 @@ function PreviewHeroCard({
4000
4031
  }
4001
4032
 
4002
4033
  // src/components/preview/PreviewPlaceholder.tsx
4003
- import * as React25 from "react";
4034
+ import * as React26 from "react";
4004
4035
  import { Animated as Animated6 } from "react-native";
4005
4036
  import { LinearGradient as LinearGradient2 } from "expo-linear-gradient";
4006
4037
  import { Fragment as Fragment3, jsx as jsx28, jsxs as jsxs15 } from "react/jsx-runtime";
4007
4038
  function PreviewPlaceholder({ visible, style }) {
4008
4039
  if (!visible) return null;
4009
- const opacityAnim = React25.useRef(new Animated6.Value(0)).current;
4010
- React25.useEffect(() => {
4040
+ const opacityAnim = React26.useRef(new Animated6.Value(0)).current;
4041
+ React26.useEffect(() => {
4011
4042
  if (!visible) return;
4012
4043
  const animation = Animated6.loop(
4013
4044
  Animated6.sequence([
@@ -4599,12 +4630,12 @@ function PreviewCustomizeSection({
4599
4630
  }
4600
4631
 
4601
4632
  // src/studio/ui/preview-panel/PreviewCollaborateSection.tsx
4602
- import * as React31 from "react";
4633
+ import * as React32 from "react";
4603
4634
  import { ActivityIndicator as ActivityIndicator6, Alert, View as View32 } from "react-native";
4604
4635
  import { Send as Send2 } from "lucide-react-native";
4605
4636
 
4606
4637
  // src/components/merge-requests/MergeRequestStatusCard.tsx
4607
- import * as React27 from "react";
4638
+ import * as React28 from "react";
4608
4639
  import { Animated as Animated7, Pressable as Pressable9, View as View28 } from "react-native";
4609
4640
  import { Ban, Check as Check3, CheckCheck, ChevronDown as ChevronDown2 } from "lucide-react-native";
4610
4641
 
@@ -4686,11 +4717,11 @@ function toIsoString(input) {
4686
4717
  }
4687
4718
 
4688
4719
  // src/components/merge-requests/useControlledExpansion.ts
4689
- import * as React26 from "react";
4720
+ import * as React27 from "react";
4690
4721
  function useControlledExpansion(props) {
4691
- const [uncontrolled, setUncontrolled] = React26.useState(false);
4722
+ const [uncontrolled, setUncontrolled] = React27.useState(false);
4692
4723
  const expanded = props.expanded ?? uncontrolled;
4693
- const setExpanded = React26.useCallback(
4724
+ const setExpanded = React27.useCallback(
4694
4725
  (next) => {
4695
4726
  var _a;
4696
4727
  (_a = props.onExpandedChange) == null ? void 0 : _a.call(props, next);
@@ -4715,8 +4746,8 @@ function MergeRequestStatusCard({
4715
4746
  const isDark = theme.scheme === "dark";
4716
4747
  const textColor = isDark ? "#FFFFFF" : "#000000";
4717
4748
  const subTextColor = isDark ? "#A1A1AA" : "#71717A";
4718
- const status = React27.useMemo(() => getMergeRequestStatusDisplay(String(mergeRequest.status)), [mergeRequest.status]);
4719
- const { StatusIcon, iconColor, bgColor, statusText } = React27.useMemo(() => {
4749
+ const status = React28.useMemo(() => getMergeRequestStatusDisplay(String(mergeRequest.status)), [mergeRequest.status]);
4750
+ const { StatusIcon, iconColor, bgColor, statusText } = React28.useMemo(() => {
4720
4751
  switch (mergeRequest.status) {
4721
4752
  case "approved":
4722
4753
  case "merged":
@@ -4747,8 +4778,8 @@ function MergeRequestStatusCard({
4747
4778
  const createdIso = toIsoString(mergeRequest.createdAt ?? null);
4748
4779
  const headerTimeAgo = updatedIso ? formatTimeAgo(updatedIso) : "";
4749
4780
  const createdTimeAgo = createdIso ? formatTimeAgo(createdIso) : "";
4750
- const rotate = React27.useRef(new Animated7.Value(expanded ? 1 : 0)).current;
4751
- React27.useEffect(() => {
4781
+ const rotate = React28.useRef(new Animated7.Value(expanded ? 1 : 0)).current;
4782
+ React28.useEffect(() => {
4752
4783
  Animated7.timing(rotate, {
4753
4784
  toValue: expanded ? 1 : 0,
4754
4785
  duration: 200,
@@ -4839,16 +4870,16 @@ function MergeRequestStatusCard({
4839
4870
  }
4840
4871
 
4841
4872
  // src/components/merge-requests/ReviewMergeRequestCarousel.tsx
4842
- import * as React30 from "react";
4873
+ import * as React31 from "react";
4843
4874
  import { Animated as Animated9, FlatList, View as View31, useWindowDimensions as useWindowDimensions3 } from "react-native";
4844
4875
 
4845
4876
  // src/components/merge-requests/ReviewMergeRequestCard.tsx
4846
- import * as React29 from "react";
4877
+ import * as React30 from "react";
4847
4878
  import { ActivityIndicator as ActivityIndicator5, Animated as Animated8, Pressable as Pressable11, View as View30 } from "react-native";
4848
4879
  import { Check as Check4, ChevronDown as ChevronDown3, Play as Play3, X as X3 } from "lucide-react-native";
4849
4880
 
4850
4881
  // src/components/merge-requests/ReviewMergeRequestActionButton.tsx
4851
- import * as React28 from "react";
4882
+ import * as React29 from "react";
4852
4883
  import { Pressable as Pressable10, View as View29 } from "react-native";
4853
4884
  import { jsx as jsx41 } from "react/jsx-runtime";
4854
4885
  function ReviewMergeRequestActionButton({
@@ -4859,7 +4890,7 @@ function ReviewMergeRequestActionButton({
4859
4890
  children,
4860
4891
  iconOnly
4861
4892
  }) {
4862
- const [pressed, setPressed] = React28.useState(false);
4893
+ const [pressed, setPressed] = React29.useState(false);
4863
4894
  const height = iconOnly ? 36 : 40;
4864
4895
  const width = iconOnly ? 36 : void 0;
4865
4896
  const paddingHorizontal = iconOnly ? 0 : 16;
@@ -4921,10 +4952,10 @@ function ReviewMergeRequestCard({
4921
4952
  onTest
4922
4953
  }) {
4923
4954
  const theme = useTheme();
4924
- const status = React29.useMemo(() => getMergeRequestStatusDisplay(mr.status), [mr.status]);
4955
+ const status = React30.useMemo(() => getMergeRequestStatusDisplay(mr.status), [mr.status]);
4925
4956
  const canAct = mr.status === "open";
4926
- const rotate = React29.useRef(new Animated8.Value(isExpanded ? 1 : 0)).current;
4927
- React29.useEffect(() => {
4957
+ const rotate = React30.useRef(new Animated8.Value(isExpanded ? 1 : 0)).current;
4958
+ React30.useEffect(() => {
4928
4959
  Animated8.timing(rotate, { toValue: isExpanded ? 1 : 0, duration: 200, useNativeDriver: true }).start();
4929
4960
  }, [isExpanded, rotate]);
4930
4961
  const position = total > 1 ? `${index + 1}/${total}` : "Merge request";
@@ -5056,11 +5087,11 @@ function ReviewMergeRequestCarousel({
5056
5087
  }) {
5057
5088
  const theme = useTheme();
5058
5089
  const { width } = useWindowDimensions3();
5059
- const [expanded, setExpanded] = React30.useState({});
5060
- const carouselScrollX = React30.useRef(new Animated9.Value(0)).current;
5090
+ const [expanded, setExpanded] = React31.useState({});
5091
+ const carouselScrollX = React31.useRef(new Animated9.Value(0)).current;
5061
5092
  const peekAmount = 24;
5062
5093
  const gap = 16;
5063
- const cardWidth = React30.useMemo(() => Math.max(1, width - theme.spacing.lg * 2 - peekAmount), [peekAmount, theme.spacing.lg, width]);
5094
+ const cardWidth = React31.useMemo(() => Math.max(1, width - theme.spacing.lg * 2 - peekAmount), [peekAmount, theme.spacing.lg, width]);
5064
5095
  const snapInterval = cardWidth + gap;
5065
5096
  const dotColor = theme.scheme === "dark" ? "#FFFFFF" : "#000000";
5066
5097
  if (mergeRequests.length === 0) return null;
@@ -5159,7 +5190,7 @@ function PreviewCollaborateSection({
5159
5190
  onTestMr
5160
5191
  }) {
5161
5192
  const theme = useTheme();
5162
- const [submittingMr, setSubmittingMr] = React31.useState(false);
5193
+ const [submittingMr, setSubmittingMr] = React32.useState(false);
5163
5194
  const hasSection = canSubmitMergeRequest || incomingMergeRequests.length > 0 || outgoingMergeRequests.length > 0;
5164
5195
  if (!hasSection) return null;
5165
5196
  const showActionsSubtitle = canSubmitMergeRequest && onSubmitMergeRequest || onTestMr && incomingMergeRequests.length > 0;
@@ -5267,7 +5298,7 @@ function PreviewCollaborateSection({
5267
5298
  }
5268
5299
 
5269
5300
  // src/studio/ui/preview-panel/usePreviewPanelData.ts
5270
- import * as React33 from "react";
5301
+ import * as React34 from "react";
5271
5302
 
5272
5303
  // src/data/apps/images/remote.ts
5273
5304
  var AppImagesRemoteDataSourceImpl = class extends BaseRemote {
@@ -5318,7 +5349,7 @@ var AppImagesRepositoryImpl = class extends BaseRepository {
5318
5349
  var appImagesRepository = new AppImagesRepositoryImpl(appImagesRemoteDataSource);
5319
5350
 
5320
5351
  // src/studio/hooks/useAppStats.ts
5321
- import * as React32 from "react";
5352
+ import * as React33 from "react";
5322
5353
  import * as Haptics2 from "expo-haptics";
5323
5354
 
5324
5355
  // src/data/likes/remote.ts
@@ -5387,34 +5418,34 @@ function useAppStats({
5387
5418
  initialIsLiked = false,
5388
5419
  onOpenComments
5389
5420
  }) {
5390
- const [likeCount, setLikeCount] = React32.useState(initialLikes);
5391
- const [commentCount, setCommentCount] = React32.useState(initialComments);
5392
- const [forkCount, setForkCount] = React32.useState(initialForks);
5393
- const [isLiked, setIsLiked] = React32.useState(initialIsLiked);
5394
- const didMutateRef = React32.useRef(false);
5395
- const lastAppIdRef = React32.useRef("");
5396
- React32.useEffect(() => {
5421
+ const [likeCount, setLikeCount] = React33.useState(initialLikes);
5422
+ const [commentCount, setCommentCount] = React33.useState(initialComments);
5423
+ const [forkCount, setForkCount] = React33.useState(initialForks);
5424
+ const [isLiked, setIsLiked] = React33.useState(initialIsLiked);
5425
+ const didMutateRef = React33.useRef(false);
5426
+ const lastAppIdRef = React33.useRef("");
5427
+ React33.useEffect(() => {
5397
5428
  if (lastAppIdRef.current === appId) return;
5398
5429
  lastAppIdRef.current = appId;
5399
5430
  didMutateRef.current = false;
5400
5431
  }, [appId]);
5401
- React32.useEffect(() => {
5432
+ React33.useEffect(() => {
5402
5433
  if (didMutateRef.current) return;
5403
5434
  setLikeCount(initialLikes);
5404
5435
  }, [appId, initialLikes]);
5405
- React32.useEffect(() => {
5436
+ React33.useEffect(() => {
5406
5437
  if (didMutateRef.current) return;
5407
5438
  setCommentCount(initialComments);
5408
5439
  }, [appId, initialComments]);
5409
- React32.useEffect(() => {
5440
+ React33.useEffect(() => {
5410
5441
  if (didMutateRef.current) return;
5411
5442
  setForkCount(initialForks);
5412
5443
  }, [appId, initialForks]);
5413
- React32.useEffect(() => {
5444
+ React33.useEffect(() => {
5414
5445
  if (didMutateRef.current) return;
5415
5446
  setIsLiked(initialIsLiked);
5416
5447
  }, [appId, initialIsLiked]);
5417
- const handleLike = React32.useCallback(async () => {
5448
+ const handleLike = React33.useCallback(async () => {
5418
5449
  var _a, _b;
5419
5450
  if (!appId) return;
5420
5451
  didMutateRef.current = true;
@@ -5438,7 +5469,7 @@ function useAppStats({
5438
5469
  setLikeCount((prev) => Math.max(0, prev + (newIsLiked ? -1 : 1)));
5439
5470
  }
5440
5471
  }, [appId, isLiked, likeCount]);
5441
- const handleOpenComments = React32.useCallback(() => {
5472
+ const handleOpenComments = React33.useCallback(() => {
5442
5473
  if (!appId) return;
5443
5474
  try {
5444
5475
  void Haptics2.impactAsync(Haptics2.ImpactFeedbackStyle.Light);
@@ -5453,11 +5484,11 @@ function useAppStats({
5453
5484
  var LIKE_DEBUG_PREFIX = "[COMERGE_LIKE_DEBUG]";
5454
5485
  function usePreviewPanelData(params) {
5455
5486
  const { app, isOwner, outgoingMergeRequests, onOpenComments, commentCountOverride } = params;
5456
- const [imageUrl, setImageUrl] = React33.useState(null);
5457
- const [imageLoaded, setImageLoaded] = React33.useState(false);
5458
- const [insights, setInsights] = React33.useState({ likes: 0, comments: 0, forks: 0, downloads: 0 });
5459
- const [creator, setCreator] = React33.useState(null);
5460
- React33.useEffect(() => {
5487
+ const [imageUrl, setImageUrl] = React34.useState(null);
5488
+ const [imageLoaded, setImageLoaded] = React34.useState(false);
5489
+ const [insights, setInsights] = React34.useState({ likes: 0, comments: 0, forks: 0, downloads: 0 });
5490
+ const [creator, setCreator] = React34.useState(null);
5491
+ React34.useEffect(() => {
5461
5492
  if (!(app == null ? void 0 : app.id)) return;
5462
5493
  let cancelled = false;
5463
5494
  (async () => {
@@ -5472,7 +5503,7 @@ function usePreviewPanelData(params) {
5472
5503
  cancelled = true;
5473
5504
  };
5474
5505
  }, [app == null ? void 0 : app.id]);
5475
- React33.useEffect(() => {
5506
+ React34.useEffect(() => {
5476
5507
  if (!(app == null ? void 0 : app.createdBy)) return;
5477
5508
  let cancelled = false;
5478
5509
  (async () => {
@@ -5488,10 +5519,10 @@ function usePreviewPanelData(params) {
5488
5519
  cancelled = true;
5489
5520
  };
5490
5521
  }, [app == null ? void 0 : app.createdBy]);
5491
- React33.useEffect(() => {
5522
+ React34.useEffect(() => {
5492
5523
  setImageLoaded(false);
5493
5524
  }, [app == null ? void 0 : app.id]);
5494
- React33.useEffect(() => {
5525
+ React34.useEffect(() => {
5495
5526
  if (!(app == null ? void 0 : app.id)) return;
5496
5527
  let cancelled = false;
5497
5528
  (async () => {
@@ -5516,7 +5547,7 @@ function usePreviewPanelData(params) {
5516
5547
  cancelled = true;
5517
5548
  };
5518
5549
  }, [app == null ? void 0 : app.id]);
5519
- React33.useEffect(() => {
5550
+ React34.useEffect(() => {
5520
5551
  if (!(app == null ? void 0 : app.id)) return;
5521
5552
  log.debug(
5522
5553
  `${LIKE_DEBUG_PREFIX} usePreviewPanelData.appChanged appId=${app.id} app.isLiked=${String(app.isLiked)}`
@@ -5530,7 +5561,7 @@ function usePreviewPanelData(params) {
5530
5561
  initialIsLiked: Boolean(app == null ? void 0 : app.isLiked),
5531
5562
  onOpenComments
5532
5563
  });
5533
- const canSubmitMergeRequest = React33.useMemo(() => {
5564
+ const canSubmitMergeRequest = React34.useMemo(() => {
5534
5565
  if (!isOwner) return false;
5535
5566
  if (!app) return false;
5536
5567
  if (!app.forkedFromAppId) return false;
@@ -5643,16 +5674,16 @@ function PreviewPanel({
5643
5674
  }
5644
5675
 
5645
5676
  // src/studio/ui/ChatPanel.tsx
5646
- import * as React38 from "react";
5677
+ import * as React39 from "react";
5647
5678
  import { ActivityIndicator as ActivityIndicator8, View as View41 } from "react-native";
5648
5679
 
5649
5680
  // src/components/chat/ChatPage.tsx
5650
- import * as React36 from "react";
5681
+ import * as React37 from "react";
5651
5682
  import { Keyboard as Keyboard4, Platform as Platform9, View as View37 } from "react-native";
5652
5683
  import { useSafeAreaInsets as useSafeAreaInsets4 } from "react-native-safe-area-context";
5653
5684
 
5654
5685
  // src/components/chat/ChatMessageList.tsx
5655
- import * as React35 from "react";
5686
+ import * as React36 from "react";
5656
5687
  import { View as View36 } from "react-native";
5657
5688
  import { BottomSheetFlatList } from "@gorhom/bottom-sheet";
5658
5689
 
@@ -5698,17 +5729,17 @@ function ChatMessageBubble({ message, renderContent, style }) {
5698
5729
  }
5699
5730
 
5700
5731
  // src/components/chat/TypingIndicator.tsx
5701
- import * as React34 from "react";
5732
+ import * as React35 from "react";
5702
5733
  import { Animated as Animated10, View as View35 } from "react-native";
5703
5734
  import { jsx as jsx47 } from "react/jsx-runtime";
5704
5735
  function TypingIndicator({ style }) {
5705
5736
  const theme = useTheme();
5706
5737
  const dotColor = theme.colors.textSubtle;
5707
- const anims = React34.useMemo(
5738
+ const anims = React35.useMemo(
5708
5739
  () => [new Animated10.Value(0.3), new Animated10.Value(0.3), new Animated10.Value(0.3)],
5709
5740
  []
5710
5741
  );
5711
- React34.useEffect(() => {
5742
+ React35.useEffect(() => {
5712
5743
  const loops = [];
5713
5744
  anims.forEach((a, idx) => {
5714
5745
  const seq = Animated10.sequence([
@@ -5742,7 +5773,7 @@ function TypingIndicator({ style }) {
5742
5773
 
5743
5774
  // src/components/chat/ChatMessageList.tsx
5744
5775
  import { jsx as jsx48, jsxs as jsxs28 } from "react/jsx-runtime";
5745
- var ChatMessageList = React35.forwardRef(
5776
+ var ChatMessageList = React36.forwardRef(
5746
5777
  ({
5747
5778
  messages,
5748
5779
  showTypingIndicator = false,
@@ -5753,20 +5784,20 @@ var ChatMessageList = React35.forwardRef(
5753
5784
  nearBottomThreshold = 200
5754
5785
  }, ref) => {
5755
5786
  const theme = useTheme();
5756
- const listRef = React35.useRef(null);
5757
- const nearBottomRef = React35.useRef(true);
5758
- const initialScrollDoneRef = React35.useRef(false);
5759
- const lastMessageIdRef = React35.useRef(null);
5760
- const data = React35.useMemo(() => {
5787
+ const listRef = React36.useRef(null);
5788
+ const nearBottomRef = React36.useRef(true);
5789
+ const initialScrollDoneRef = React36.useRef(false);
5790
+ const lastMessageIdRef = React36.useRef(null);
5791
+ const data = React36.useMemo(() => {
5761
5792
  return [...messages].reverse();
5762
5793
  }, [messages]);
5763
- const scrollToBottom = React35.useCallback((options) => {
5794
+ const scrollToBottom = React36.useCallback((options) => {
5764
5795
  var _a;
5765
5796
  const animated = (options == null ? void 0 : options.animated) ?? true;
5766
5797
  (_a = listRef.current) == null ? void 0 : _a.scrollToOffset({ offset: 0, animated });
5767
5798
  }, []);
5768
- React35.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
5769
- const handleScroll = React35.useCallback(
5799
+ React36.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
5800
+ const handleScroll = React36.useCallback(
5770
5801
  (e) => {
5771
5802
  const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;
5772
5803
  const distanceFromBottom = Math.max(contentOffset.y - Math.max(bottomInset, 0), 0);
@@ -5778,7 +5809,7 @@ var ChatMessageList = React35.forwardRef(
5778
5809
  },
5779
5810
  [bottomInset, nearBottomThreshold, onNearBottomChange]
5780
5811
  );
5781
- React35.useEffect(() => {
5812
+ React36.useEffect(() => {
5782
5813
  if (!initialScrollDoneRef.current) return;
5783
5814
  const lastId = messages.length > 0 ? messages[messages.length - 1].id : null;
5784
5815
  const prevLastId = lastMessageIdRef.current;
@@ -5788,7 +5819,7 @@ var ChatMessageList = React35.forwardRef(
5788
5819
  const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
5789
5820
  return () => cancelAnimationFrame(id);
5790
5821
  }, [messages, scrollToBottom]);
5791
- React35.useEffect(() => {
5822
+ React36.useEffect(() => {
5792
5823
  if (showTypingIndicator && nearBottomRef.current) {
5793
5824
  const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
5794
5825
  return () => cancelAnimationFrame(id);
@@ -5850,9 +5881,9 @@ function ChatPage({
5850
5881
  }) {
5851
5882
  const theme = useTheme();
5852
5883
  const insets = useSafeAreaInsets4();
5853
- const [composerHeight, setComposerHeight] = React36.useState(0);
5854
- const [keyboardVisible, setKeyboardVisible] = React36.useState(false);
5855
- React36.useEffect(() => {
5884
+ const [composerHeight, setComposerHeight] = React37.useState(0);
5885
+ const [keyboardVisible, setKeyboardVisible] = React37.useState(false);
5886
+ React37.useEffect(() => {
5856
5887
  if (Platform9.OS !== "ios") return;
5857
5888
  const show = Keyboard4.addListener("keyboardWillShow", () => setKeyboardVisible(true));
5858
5889
  const hide = Keyboard4.addListener("keyboardWillHide", () => setKeyboardVisible(false));
@@ -5864,12 +5895,12 @@ function ChatPage({
5864
5895
  const footerBottomPadding = Platform9.OS === "ios" ? keyboardVisible ? 0 : insets.bottom : insets.bottom + 10;
5865
5896
  const overlayBottom = composerHeight + footerBottomPadding + theme.spacing.lg;
5866
5897
  const bottomInset = composerHeight + footerBottomPadding + theme.spacing.xl;
5867
- const resolvedOverlay = React36.useMemo(() => {
5898
+ const resolvedOverlay = React37.useMemo(() => {
5868
5899
  var _a;
5869
5900
  if (!overlay) return null;
5870
- if (!React36.isValidElement(overlay)) return overlay;
5901
+ if (!React37.isValidElement(overlay)) return overlay;
5871
5902
  const prevStyle = (_a = overlay.props) == null ? void 0 : _a.style;
5872
- return React36.cloneElement(overlay, {
5903
+ return React37.cloneElement(overlay, {
5873
5904
  style: [prevStyle, { bottom: overlayBottom }]
5874
5905
  });
5875
5906
  }, [overlay, overlayBottom]);
@@ -5924,15 +5955,15 @@ function ChatPage({
5924
5955
  }
5925
5956
 
5926
5957
  // src/components/chat/ScrollToBottomButton.tsx
5927
- import * as React37 from "react";
5958
+ import * as React38 from "react";
5928
5959
  import { Pressable as Pressable12, View as View38 } from "react-native";
5929
5960
  import Animated11, { Easing as Easing2, useAnimatedStyle as useAnimatedStyle2, useSharedValue as useSharedValue2, withTiming as withTiming2 } from "react-native-reanimated";
5930
5961
  import { jsx as jsx50 } from "react/jsx-runtime";
5931
5962
  function ScrollToBottomButton({ visible, onPress, children, style }) {
5932
5963
  const theme = useTheme();
5933
5964
  const progress = useSharedValue2(visible ? 1 : 0);
5934
- const [pressed, setPressed] = React37.useState(false);
5935
- React37.useEffect(() => {
5965
+ const [pressed, setPressed] = React38.useState(false);
5966
+ React38.useEffect(() => {
5936
5967
  progress.value = withTiming2(visible ? 1 : 0, { duration: 200, easing: Easing2.out(Easing2.ease) });
5937
5968
  }, [progress, visible]);
5938
5969
  const animStyle = useAnimatedStyle2(() => ({
@@ -6087,9 +6118,9 @@ function ChatPanel({
6087
6118
  onStartDraw,
6088
6119
  onSend
6089
6120
  }) {
6090
- const listRef = React38.useRef(null);
6091
- const [nearBottom, setNearBottom] = React38.useState(true);
6092
- const handleSend = React38.useCallback(
6121
+ const listRef = React39.useRef(null);
6122
+ const [nearBottom, setNearBottom] = React39.useState(true);
6123
+ const handleSend = React39.useCallback(
6093
6124
  async (text, composerAttachments) => {
6094
6125
  const all = composerAttachments ?? attachments;
6095
6126
  await onSend(text, all.length > 0 ? all : void 0);
@@ -6103,7 +6134,7 @@ function ChatPanel({
6103
6134
  },
6104
6135
  [attachments, nearBottom, onClearAttachments, onSend]
6105
6136
  );
6106
- const handleScrollToBottom = React38.useCallback(() => {
6137
+ const handleScrollToBottom = React39.useCallback(() => {
6107
6138
  var _a;
6108
6139
  (_a = listRef.current) == null ? void 0 : _a.scrollToBottom({ animated: true });
6109
6140
  }, []);
@@ -6177,7 +6208,7 @@ function ChatPanel({
6177
6208
  }
6178
6209
 
6179
6210
  // src/components/dialogs/ConfirmMergeRequestDialog.tsx
6180
- import * as React39 from "react";
6211
+ import * as React40 from "react";
6181
6212
  import { Pressable as Pressable14, View as View43 } from "react-native";
6182
6213
 
6183
6214
  // src/components/primitives/Modal.tsx
@@ -6229,14 +6260,14 @@ function ConfirmMergeRequestDialog({
6229
6260
  onTestFirst
6230
6261
  }) {
6231
6262
  const theme = useTheme();
6232
- const close = React39.useCallback(() => onOpenChange(false), [onOpenChange]);
6263
+ const close = React40.useCallback(() => onOpenChange(false), [onOpenChange]);
6233
6264
  const canConfirm = Boolean(mergeRequest) && !approveDisabled;
6234
- const handleConfirm = React39.useCallback(() => {
6265
+ const handleConfirm = React40.useCallback(() => {
6235
6266
  if (!mergeRequest) return;
6236
6267
  onOpenChange(false);
6237
6268
  void onConfirm();
6238
6269
  }, [mergeRequest, onConfirm, onOpenChange]);
6239
- const handleTestFirst = React39.useCallback(() => {
6270
+ const handleTestFirst = React40.useCallback(() => {
6240
6271
  if (!mergeRequest) return;
6241
6272
  onOpenChange(false);
6242
6273
  void onTestFirst(mergeRequest);
@@ -6385,7 +6416,7 @@ function ConfirmMergeFlow({
6385
6416
  }
6386
6417
 
6387
6418
  // src/studio/hooks/useOptimisticChatMessages.ts
6388
- import * as React40 from "react";
6419
+ import * as React41 from "react";
6389
6420
  function makeOptimisticId() {
6390
6421
  return `optimistic:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 10)}`;
6391
6422
  }
@@ -6423,11 +6454,11 @@ function useOptimisticChatMessages({
6423
6454
  chatMessages,
6424
6455
  onSendChat
6425
6456
  }) {
6426
- const [optimisticChat, setOptimisticChat] = React40.useState([]);
6427
- React40.useEffect(() => {
6457
+ const [optimisticChat, setOptimisticChat] = React41.useState([]);
6458
+ React41.useEffect(() => {
6428
6459
  setOptimisticChat([]);
6429
6460
  }, [threadId]);
6430
- const messages = React40.useMemo(() => {
6461
+ const messages = React41.useMemo(() => {
6431
6462
  if (!optimisticChat || optimisticChat.length === 0) return chatMessages;
6432
6463
  const unresolved = optimisticChat.filter((o) => !isOptimisticResolvedByServer(chatMessages, o));
6433
6464
  if (unresolved.length === 0) return chatMessages;
@@ -6443,7 +6474,7 @@ function useOptimisticChatMessages({
6443
6474
  merged.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
6444
6475
  return merged;
6445
6476
  }, [chatMessages, optimisticChat]);
6446
- React40.useEffect(() => {
6477
+ React41.useEffect(() => {
6447
6478
  if (optimisticChat.length === 0) return;
6448
6479
  setOptimisticChat((prev) => {
6449
6480
  if (prev.length === 0) return prev;
@@ -6451,7 +6482,7 @@ function useOptimisticChatMessages({
6451
6482
  return next.length === prev.length ? prev : next;
6452
6483
  });
6453
6484
  }, [chatMessages, optimisticChat.length]);
6454
- const onSend = React40.useCallback(
6485
+ const onSend = React41.useCallback(
6455
6486
  async (text, attachments) => {
6456
6487
  if (shouldForkOnEdit) {
6457
6488
  await onSendChat(text, attachments);
@@ -6503,18 +6534,18 @@ function StudioOverlay({
6503
6534
  chatShowTypingIndicator,
6504
6535
  onSendChat,
6505
6536
  onNavigateHome,
6506
- showFloatingButton,
6537
+ showBubble,
6507
6538
  studioControlOptions
6508
6539
  }) {
6509
6540
  const theme = useTheme();
6510
6541
  const { width } = useWindowDimensions4();
6511
- const [sheetOpen, setSheetOpen] = React41.useState(false);
6512
- const sheetOpenRef = React41.useRef(sheetOpen);
6513
- const [activePage, setActivePage] = React41.useState("preview");
6514
- const [drawing, setDrawing] = React41.useState(false);
6515
- const [chatAttachments, setChatAttachments] = React41.useState([]);
6516
- const [commentsAppId, setCommentsAppId] = React41.useState(null);
6517
- const [commentsCount, setCommentsCount] = React41.useState(null);
6542
+ const [sheetOpen, setSheetOpen] = React42.useState(false);
6543
+ const sheetOpenRef = React42.useRef(sheetOpen);
6544
+ const [activePage, setActivePage] = React42.useState("preview");
6545
+ const [drawing, setDrawing] = React42.useState(false);
6546
+ const [chatAttachments, setChatAttachments] = React42.useState([]);
6547
+ const [commentsAppId, setCommentsAppId] = React42.useState(null);
6548
+ const [commentsCount, setCommentsCount] = React42.useState(null);
6518
6549
  const threadId = (app == null ? void 0 : app.threadId) ?? null;
6519
6550
  const optimistic = useOptimisticChatMessages({
6520
6551
  threadId,
@@ -6522,24 +6553,24 @@ function StudioOverlay({
6522
6553
  chatMessages,
6523
6554
  onSendChat
6524
6555
  });
6525
- const [confirmMrId, setConfirmMrId] = React41.useState(null);
6526
- const confirmMr = React41.useMemo(
6556
+ const [confirmMrId, setConfirmMrId] = React42.useState(null);
6557
+ const confirmMr = React42.useMemo(
6527
6558
  () => confirmMrId ? incomingMergeRequests.find((m) => m.id === confirmMrId) ?? null : null,
6528
6559
  [confirmMrId, incomingMergeRequests]
6529
6560
  );
6530
- const handleSheetOpenChange = React41.useCallback((open) => {
6561
+ const handleSheetOpenChange = React42.useCallback((open) => {
6531
6562
  setSheetOpen(open);
6532
6563
  if (!open) Keyboard5.dismiss();
6533
6564
  }, []);
6534
- const closeSheet = React41.useCallback(() => {
6565
+ const closeSheet = React42.useCallback(() => {
6535
6566
  handleSheetOpenChange(false);
6536
6567
  }, [handleSheetOpenChange]);
6537
- const openSheet = React41.useCallback(() => setSheetOpen(true), []);
6538
- const goToChat = React41.useCallback(() => {
6568
+ const openSheet = React42.useCallback(() => setSheetOpen(true), []);
6569
+ const goToChat = React42.useCallback(() => {
6539
6570
  setActivePage("chat");
6540
6571
  openSheet();
6541
6572
  }, [openSheet]);
6542
- const backToPreview = React41.useCallback(() => {
6573
+ const backToPreview = React42.useCallback(() => {
6543
6574
  if (Platform10.OS !== "ios") {
6544
6575
  Keyboard5.dismiss();
6545
6576
  setActivePage("preview");
@@ -6557,11 +6588,11 @@ function StudioOverlay({
6557
6588
  const t = setTimeout(finalize, 350);
6558
6589
  Keyboard5.dismiss();
6559
6590
  }, []);
6560
- const startDraw = React41.useCallback(() => {
6591
+ const startDraw = React42.useCallback(() => {
6561
6592
  setDrawing(true);
6562
6593
  closeSheet();
6563
6594
  }, [closeSheet]);
6564
- const handleDrawCapture = React41.useCallback(
6595
+ const handleDrawCapture = React42.useCallback(
6565
6596
  (dataUrl) => {
6566
6597
  setChatAttachments((prev) => [...prev, dataUrl]);
6567
6598
  setDrawing(false);
@@ -6570,7 +6601,7 @@ function StudioOverlay({
6570
6601
  },
6571
6602
  [openSheet]
6572
6603
  );
6573
- const toggleSheet = React41.useCallback(async () => {
6604
+ const toggleSheet = React42.useCallback(async () => {
6574
6605
  if (!sheetOpen) {
6575
6606
  const shouldExitTest = Boolean(testingMrId) || isTesting;
6576
6607
  if (shouldExitTest) {
@@ -6582,7 +6613,7 @@ function StudioOverlay({
6582
6613
  closeSheet();
6583
6614
  }
6584
6615
  }, [closeSheet, isTesting, onRestoreBase, sheetOpen, testingMrId]);
6585
- const handleTestMr = React41.useCallback(
6616
+ const handleTestMr = React42.useCallback(
6586
6617
  async (mr) => {
6587
6618
  if (!onTestMr) return;
6588
6619
  await onTestMr(mr);
@@ -6590,10 +6621,10 @@ function StudioOverlay({
6590
6621
  },
6591
6622
  [closeSheet, onTestMr]
6592
6623
  );
6593
- React41.useEffect(() => {
6624
+ React42.useEffect(() => {
6594
6625
  sheetOpenRef.current = sheetOpen;
6595
6626
  }, [sheetOpen]);
6596
- React41.useEffect(() => {
6627
+ React42.useEffect(() => {
6597
6628
  const poller = startStudioControlPolling((action) => {
6598
6629
  if (action === "show" && !sheetOpenRef.current) openSheet();
6599
6630
  if (action === "hide" && sheetOpenRef.current) closeSheet();
@@ -6601,7 +6632,7 @@ function StudioOverlay({
6601
6632
  }, studioControlOptions);
6602
6633
  return () => poller.stop();
6603
6634
  }, [closeSheet, openSheet, studioControlOptions, toggleSheet]);
6604
- React41.useEffect(() => {
6635
+ React42.useEffect(() => {
6605
6636
  void publishComergeStudioUIState(sheetOpen, studioControlOptions);
6606
6637
  }, [sheetOpen, studioControlOptions]);
6607
6638
  return /* @__PURE__ */ jsxs34(Fragment6, { children: [
@@ -6660,8 +6691,8 @@ function StudioOverlay({
6660
6691
  )
6661
6692
  }
6662
6693
  ) }),
6663
- showFloatingButton && /* @__PURE__ */ jsx57(
6664
- FloatingDraggableButton,
6694
+ showBubble && /* @__PURE__ */ jsx57(
6695
+ Bubble,
6665
6696
  {
6666
6697
  visible: !sheetOpen && !drawing,
6667
6698
  ariaLabel: sheetOpen ? "Hide studio" : "Show studio",
@@ -6709,24 +6740,25 @@ function StudioOverlay({
6709
6740
  import { jsx as jsx58, jsxs as jsxs35 } from "react/jsx-runtime";
6710
6741
  function ComergeStudio({
6711
6742
  appId,
6712
- apiKey,
6743
+ clientKey: clientKey2,
6713
6744
  appKey = "MicroMain",
6714
6745
  onNavigateHome,
6715
6746
  style,
6716
- showFloatingButton = true,
6717
- studioControlOptions
6747
+ showBubble = true,
6748
+ studioControlOptions,
6749
+ embeddedBaseBundles
6718
6750
  }) {
6719
- const [activeAppId, setActiveAppId] = React42.useState(appId);
6720
- const [runtimeAppId, setRuntimeAppId] = React42.useState(appId);
6721
- const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React42.useState(null);
6722
- const platform = React42.useMemo(() => RNPlatform.OS === "ios" ? "ios" : "android", []);
6723
- React42.useEffect(() => {
6751
+ const [activeAppId, setActiveAppId] = React43.useState(appId);
6752
+ const [runtimeAppId, setRuntimeAppId] = React43.useState(appId);
6753
+ const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React43.useState(null);
6754
+ const platform = React43.useMemo(() => RNPlatform.OS === "ios" ? "ios" : "android", []);
6755
+ React43.useEffect(() => {
6724
6756
  setActiveAppId(appId);
6725
6757
  setRuntimeAppId(appId);
6726
6758
  setPendingRuntimeTargetAppId(null);
6727
6759
  }, [appId]);
6728
- const captureTargetRef = React42.useRef(null);
6729
- return /* @__PURE__ */ jsx58(StudioBootstrap, { apiKey, children: ({ userId }) => /* @__PURE__ */ jsx58(BottomSheetModalProvider, { children: /* @__PURE__ */ jsx58(LiquidGlassResetProvider, { resetTriggers: [appId, activeAppId, runtimeAppId], children: /* @__PURE__ */ jsx58(
6760
+ const captureTargetRef = React43.useRef(null);
6761
+ return /* @__PURE__ */ jsx58(StudioBootstrap, { clientKey: clientKey2, fallback: /* @__PURE__ */ jsx58(View45, { style: { flex: 1 } }), children: ({ userId }) => /* @__PURE__ */ jsx58(BottomSheetModalProvider, { children: /* @__PURE__ */ jsx58(LiquidGlassResetProvider, { resetTriggers: [appId, activeAppId, runtimeAppId], children: /* @__PURE__ */ jsx58(
6730
6762
  ComergeStudioInner,
6731
6763
  {
6732
6764
  userId,
@@ -6741,8 +6773,9 @@ function ComergeStudio({
6741
6773
  onNavigateHome,
6742
6774
  captureTargetRef,
6743
6775
  style,
6744
- showFloatingButton,
6745
- studioControlOptions
6776
+ showBubble,
6777
+ studioControlOptions,
6778
+ embeddedBaseBundles
6746
6779
  }
6747
6780
  ) }) }) });
6748
6781
  }
@@ -6759,17 +6792,18 @@ function ComergeStudioInner({
6759
6792
  onNavigateHome,
6760
6793
  captureTargetRef,
6761
6794
  style,
6762
- showFloatingButton,
6763
- studioControlOptions
6795
+ showBubble,
6796
+ studioControlOptions,
6797
+ embeddedBaseBundles
6764
6798
  }) {
6765
6799
  const { app, loading: appLoading } = useApp(activeAppId);
6766
6800
  const { app: runtimeAppFromHook } = useApp(runtimeAppId, { enabled: runtimeAppId !== activeAppId });
6767
6801
  const runtimeApp = runtimeAppId === activeAppId ? app : runtimeAppFromHook;
6768
- const sawEditingOnPendingTargetRef = React42.useRef(false);
6769
- React42.useEffect(() => {
6802
+ const sawEditingOnPendingTargetRef = React43.useRef(false);
6803
+ React43.useEffect(() => {
6770
6804
  sawEditingOnPendingTargetRef.current = false;
6771
6805
  }, [pendingRuntimeTargetAppId]);
6772
- React42.useEffect(() => {
6806
+ React43.useEffect(() => {
6773
6807
  if (!pendingRuntimeTargetAppId) return;
6774
6808
  if (activeAppId !== pendingRuntimeTargetAppId) return;
6775
6809
  if ((app == null ? void 0 : app.status) === "editing") {
@@ -6784,15 +6818,16 @@ function ComergeStudioInner({
6784
6818
  const bundle = useBundleManager({
6785
6819
  base: { appId: runtimeAppId, commitId: (runtimeApp == null ? void 0 : runtimeApp.headCommitId) ?? void 0 },
6786
6820
  platform,
6787
- canRequestLatest: (runtimeApp == null ? void 0 : runtimeApp.status) === "ready"
6821
+ canRequestLatest: (runtimeApp == null ? void 0 : runtimeApp.status) === "ready",
6822
+ embeddedBaseBundles
6788
6823
  });
6789
- const sawEditingOnActiveAppRef = React42.useRef(false);
6790
- const [showPostEditPreparing, setShowPostEditPreparing] = React42.useState(false);
6791
- React42.useEffect(() => {
6824
+ const sawEditingOnActiveAppRef = React43.useRef(false);
6825
+ const [showPostEditPreparing, setShowPostEditPreparing] = React43.useState(false);
6826
+ React43.useEffect(() => {
6792
6827
  sawEditingOnActiveAppRef.current = false;
6793
6828
  setShowPostEditPreparing(false);
6794
6829
  }, [activeAppId]);
6795
- React42.useEffect(() => {
6830
+ React43.useEffect(() => {
6796
6831
  if (!(app == null ? void 0 : app.id)) return;
6797
6832
  if (app.status === "editing") {
6798
6833
  sawEditingOnActiveAppRef.current = true;
@@ -6804,7 +6839,7 @@ function ComergeStudioInner({
6804
6839
  sawEditingOnActiveAppRef.current = false;
6805
6840
  }
6806
6841
  }, [app == null ? void 0 : app.id, app == null ? void 0 : app.status]);
6807
- React42.useEffect(() => {
6842
+ React43.useEffect(() => {
6808
6843
  if (!showPostEditPreparing) return;
6809
6844
  const stillProcessingBaseBundle = bundle.loading && bundle.loadingMode === "base" && !bundle.isTesting;
6810
6845
  if (!stillProcessingBaseBundle) {
@@ -6814,10 +6849,10 @@ function ComergeStudioInner({
6814
6849
  const threadId = (app == null ? void 0 : app.threadId) ?? "";
6815
6850
  const thread = useThreadMessages(threadId);
6816
6851
  const mergeRequests = useMergeRequests({ appId: activeAppId });
6817
- const hasOpenOutgoingMr = React42.useMemo(() => {
6852
+ const hasOpenOutgoingMr = React43.useMemo(() => {
6818
6853
  return mergeRequests.lists.outgoing.some((mr) => mr.status === "open");
6819
6854
  }, [mergeRequests.lists.outgoing]);
6820
- const incomingReviewMrs = React42.useMemo(() => {
6855
+ const incomingReviewMrs = React43.useMemo(() => {
6821
6856
  if (!userId) return mergeRequests.lists.incoming;
6822
6857
  return mergeRequests.lists.incoming.filter((mr) => mr.createdBy !== userId);
6823
6858
  }, [mergeRequests.lists.incoming, userId]);
@@ -6839,9 +6874,9 @@ function ComergeStudioInner({
6839
6874
  uploadAttachments: uploader.uploadBase64Images
6840
6875
  });
6841
6876
  const chatSendDisabled = hasNoOutcomeAfterLastHuman(thread.raw);
6842
- const [processingMrId, setProcessingMrId] = React42.useState(null);
6843
- const [testingMrId, setTestingMrId] = React42.useState(null);
6844
- const chatShowTypingIndicator = React42.useMemo(() => {
6877
+ const [processingMrId, setProcessingMrId] = React43.useState(null);
6878
+ const [testingMrId, setTestingMrId] = React43.useState(null);
6879
+ const chatShowTypingIndicator = React43.useMemo(() => {
6845
6880
  var _a;
6846
6881
  if (!thread.raw || thread.raw.length === 0) return false;
6847
6882
  const last = thread.raw[thread.raw.length - 1];
@@ -6855,7 +6890,8 @@ function ComergeStudioInner({
6855
6890
  appKey,
6856
6891
  bundlePath: bundle.bundlePath,
6857
6892
  forcePreparing: showPostEditPreparing,
6858
- renderToken: bundle.renderToken
6893
+ renderToken: bundle.renderToken,
6894
+ allowInitialPreparing: !embeddedBaseBundles
6859
6895
  }
6860
6896
  ),
6861
6897
  /* @__PURE__ */ jsx58(
@@ -6911,7 +6947,7 @@ function ComergeStudioInner({
6911
6947
  chatShowTypingIndicator,
6912
6948
  onSendChat: (text, attachments) => actions.sendEdit({ prompt: text, attachments }),
6913
6949
  onNavigateHome,
6914
- showFloatingButton,
6950
+ showBubble,
6915
6951
  studioControlOptions
6916
6952
  }
6917
6953
  )