@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.js CHANGED
@@ -36,7 +36,7 @@ __export(index_exports, {
36
36
  module.exports = __toCommonJS(index_exports);
37
37
 
38
38
  // src/studio/ComergeStudio.tsx
39
- var React42 = __toESM(require("react"));
39
+ var React43 = __toESM(require("react"));
40
40
  var import_react_native55 = require("react-native");
41
41
  var import_bottom_sheet6 = require("@gorhom/bottom-sheet");
42
42
 
@@ -218,11 +218,11 @@ var React = __toESM(require("react"));
218
218
  var import_axios = __toESM(require("axios"));
219
219
 
220
220
  // src/core/services/http/baseUrl.ts
221
- var BASE_URL = "https://comerge.ai";
221
+ var BASE_URL = "https://api.comerge.ai";
222
222
 
223
223
  // src/core/services/http/public.ts
224
224
  var CLIENT_KEY_HEADER = "x-comerge-api-key";
225
- var clientApiKey = null;
225
+ var clientKey = null;
226
226
  var publicApi = import_axios.default.create({
227
227
  baseURL: BASE_URL,
228
228
  timeout: 3e4,
@@ -231,19 +231,19 @@ var publicApi = import_axios.default.create({
231
231
  "Content-Type": "application/json"
232
232
  }
233
233
  });
234
- function setClientApiKey(apiKey) {
234
+ function setClientKey(clientKeyInput) {
235
235
  var _a;
236
- const trimmed = ((_a = apiKey == null ? void 0 : apiKey.trim) == null ? void 0 : _a.call(apiKey)) ?? "";
236
+ const trimmed = ((_a = clientKeyInput == null ? void 0 : clientKeyInput.trim) == null ? void 0 : _a.call(clientKeyInput)) ?? "";
237
237
  if (!trimmed) {
238
- throw new Error("comerge-studio: apiKey is required");
238
+ throw new Error("comerge-studio: clientKey is required");
239
239
  }
240
- clientApiKey = trimmed;
240
+ clientKey = trimmed;
241
241
  publicApi.defaults.headers.common[CLIENT_KEY_HEADER] = trimmed;
242
242
  }
243
243
  publicApi.interceptors.request.use((config) => {
244
- if (!clientApiKey) return config;
244
+ if (!clientKey) return config;
245
245
  config.headers = config.headers ?? {};
246
- config.headers[CLIENT_KEY_HEADER] = clientApiKey;
246
+ config.headers[CLIENT_KEY_HEADER] = clientKey;
247
247
  return config;
248
248
  });
249
249
 
@@ -308,42 +308,9 @@ async function ensureAnonymousSession() {
308
308
  return { user: data.user, isNew: true };
309
309
  }
310
310
 
311
- // src/data/base-repository.ts
312
- var BaseRepository = class {
313
- unwrapOrThrow(res) {
314
- if (res.success && res.responseObject) return res.responseObject;
315
- const msg = res.message || "Request failed";
316
- throw new Error(msg);
317
- }
318
- };
319
-
320
- // src/data/base-remote.ts
321
- var BaseRemote = class {
322
- };
323
-
324
- // src/data/public/studio-config/remote.ts
325
- var StudioConfigRemoteDataSourceImpl = class extends BaseRemote {
326
- async get() {
327
- const { data } = await publicApi.get("/v1/public/studio-config");
328
- return data;
329
- }
330
- };
331
- var studioConfigRemoteDataSource = new StudioConfigRemoteDataSourceImpl();
332
-
333
- // src/data/public/studio-config/repository.ts
334
- var StudioConfigRepositoryImpl = class extends BaseRepository {
335
- constructor(remote) {
336
- super();
337
- this.remote = remote;
338
- }
339
- async get() {
340
- const res = await this.remote.get();
341
- return this.unwrapOrThrow(res);
342
- }
343
- };
344
- var studioConfigRepository = new StudioConfigRepositoryImpl(studioConfigRemoteDataSource);
345
-
346
311
  // src/studio/bootstrap/useStudioBootstrap.ts
312
+ var SUPABASE_URL = "https://xtfxwbckjpfmqubnsusu.supabase.co";
313
+ var SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6Inh0Znh3YmNranBmbXF1Ym5zdXN1Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjA2MDEyMzAsImV4cCI6MjA3NjE3NzIzMH0.dzWGAWrK4CvrmHVHzf8w7JlUZohdap0ZPnLZnABMV8s";
347
314
  function useStudioBootstrap(options) {
348
315
  const [state, setState] = React.useState({
349
316
  ready: false,
@@ -354,11 +321,10 @@ function useStudioBootstrap(options) {
354
321
  let cancelled = false;
355
322
  (async () => {
356
323
  try {
357
- setClientApiKey(options.apiKey);
324
+ setClientKey(options.clientKey);
358
325
  const requireAuth = isSupabaseClientInjected();
359
326
  if (!requireAuth) {
360
- const cfg = await studioConfigRepository.get();
361
- setSupabaseConfig(cfg);
327
+ setSupabaseConfig({ url: SUPABASE_URL, anonKey: SUPABASE_ANON_KEY });
362
328
  }
363
329
  const { user } = requireAuth ? await ensureAuthenticatedSession() : await ensureAnonymousSession();
364
330
  if (cancelled) return;
@@ -372,14 +338,14 @@ function useStudioBootstrap(options) {
372
338
  return () => {
373
339
  cancelled = true;
374
340
  };
375
- }, [options.apiKey]);
341
+ }, [options.clientKey]);
376
342
  return state;
377
343
  }
378
344
 
379
345
  // src/studio/bootstrap/StudioBootstrap.tsx
380
346
  var import_jsx_runtime2 = require("react/jsx-runtime");
381
- function StudioBootstrap({ children, fallback, renderError, apiKey }) {
382
- const { ready, error, userId } = useStudioBootstrap({ apiKey });
347
+ function StudioBootstrap({ children, fallback, renderError, clientKey: clientKey2 }) {
348
+ const { ready, error, userId } = useStudioBootstrap({ clientKey: clientKey2 });
383
349
  if (error) {
384
350
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_native3.View, { style: { flex: 1, justifyContent: "center", alignItems: "center", padding: 24 }, children: renderError ? renderError(error) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { variant: "bodyMuted", children: error.message }) });
385
351
  }
@@ -527,6 +493,10 @@ var createApiClient = (baseURL) => {
527
493
  };
528
494
  var api = createApiClient(BASE_URL);
529
495
 
496
+ // src/data/base-remote.ts
497
+ var BaseRemote = class {
498
+ };
499
+
530
500
  // src/data/apps/remote.ts
531
501
  var AppsRemoteDataSourceImpl = class extends BaseRemote {
532
502
  async list(projectId) {
@@ -582,6 +552,15 @@ var AppsRemoteDataSourceImpl = class extends BaseRemote {
582
552
  };
583
553
  var appsRemoteDataSource = new AppsRemoteDataSourceImpl();
584
554
 
555
+ // src/data/base-repository.ts
556
+ var BaseRepository = class {
557
+ unwrapOrThrow(res) {
558
+ if (res.success && res.responseObject) return res.responseObject;
559
+ const msg = res.message || "Request failed";
560
+ throw new Error(msg);
561
+ }
562
+ };
563
+
585
564
  // src/data/apps/repository.ts
586
565
  function mapDbAppRow(row) {
587
566
  return {
@@ -934,6 +913,7 @@ function useThreadMessages(threadId) {
934
913
  // src/studio/hooks/useBundleManager.ts
935
914
  var React5 = __toESM(require("react"));
936
915
  var FileSystem = __toESM(require("expo-file-system/legacy"));
916
+ var import_expo_asset = require("expo-asset");
937
917
 
938
918
  // src/data/apps/bundles/remote.ts
939
919
  var BundlesRemoteDataSourceImpl = class extends BaseRemote {
@@ -1092,6 +1072,24 @@ async function deleteFileIfExists(fileUri) {
1092
1072
  } catch {
1093
1073
  }
1094
1074
  }
1075
+ async function hydrateBaseFromEmbeddedAsset(appId, platform, embedded) {
1076
+ if (!(embedded == null ? void 0 : embedded.module)) return null;
1077
+ const key = baseBundleKey(appId, platform);
1078
+ const targetUri = toBundleFileUri(key);
1079
+ const existing = await getExistingNonEmptyFileUri(targetUri);
1080
+ if (existing) return { bundlePath: existing, meta: embedded.meta ?? null };
1081
+ const asset = import_expo_asset.Asset.fromModule(embedded.module);
1082
+ await asset.downloadAsync();
1083
+ const sourceUri = asset.localUri ?? asset.uri;
1084
+ if (!sourceUri) return null;
1085
+ const info = await FileSystem.getInfoAsync(sourceUri);
1086
+ if (!info.exists) return null;
1087
+ await deleteFileIfExists(targetUri);
1088
+ await FileSystem.copyAsync({ from: sourceUri, to: targetUri });
1089
+ const finalUri = await getExistingNonEmptyFileUri(targetUri);
1090
+ if (!finalUri) return null;
1091
+ return { bundlePath: finalUri, meta: embedded.meta ?? null };
1092
+ }
1095
1093
  async function safeReplaceFileFromUrl(url, targetUri, tmpKey) {
1096
1094
  const tmpUri = toBundleFileUri(`tmp:${tmpKey}:${Date.now()}`);
1097
1095
  try {
@@ -1164,7 +1162,8 @@ async function resolveBundlePath(src, platform, mode) {
1164
1162
  function useBundleManager({
1165
1163
  base,
1166
1164
  platform,
1167
- canRequestLatest = true
1165
+ canRequestLatest = true,
1166
+ embeddedBaseBundles
1168
1167
  }) {
1169
1168
  const [bundlePath, setBundlePath] = React5.useState(null);
1170
1169
  const [renderToken, setRenderToken] = React5.useState(0);
@@ -1175,6 +1174,8 @@ function useBundleManager({
1175
1174
  const [isTesting, setIsTesting] = React5.useState(false);
1176
1175
  const baseRef = React5.useRef(base);
1177
1176
  baseRef.current = base;
1177
+ const embeddedBaseBundlesRef = React5.useRef(embeddedBaseBundles);
1178
+ embeddedBaseBundlesRef.current = embeddedBaseBundles;
1178
1179
  const baseOpIdRef = React5.useRef(0);
1179
1180
  const testOpIdRef = React5.useRef(0);
1180
1181
  const activeLoadModeRef = React5.useRef(null);
@@ -1197,16 +1198,29 @@ function useBundleManager({
1197
1198
  const hasCompletedFirstNetworkBaseLoadRef = React5.useRef(false);
1198
1199
  const hydrateBaseFromDisk = React5.useCallback(
1199
1200
  async (appId, reason) => {
1201
+ var _a;
1200
1202
  try {
1201
1203
  const dir = bundlesCacheDir();
1202
1204
  await ensureDir(dir);
1203
1205
  const key = baseBundleKey(appId, platform);
1204
1206
  const uri = toBundleFileUri(key);
1205
- const existing = await getExistingNonEmptyFileUri(uri);
1207
+ let existing = await getExistingNonEmptyFileUri(uri);
1208
+ let embeddedMeta = null;
1209
+ if (!existing) {
1210
+ const embedded = (_a = embeddedBaseBundlesRef.current) == null ? void 0 : _a[platform];
1211
+ const hydrated = await hydrateBaseFromEmbeddedAsset(appId, platform, embedded);
1212
+ if (hydrated == null ? void 0 : hydrated.bundlePath) {
1213
+ existing = hydrated.bundlePath;
1214
+ embeddedMeta = hydrated.meta ?? null;
1215
+ if (embeddedMeta) {
1216
+ await writeJsonFile(toBundleMetaFileUri(key), embeddedMeta);
1217
+ }
1218
+ }
1219
+ }
1206
1220
  if (existing) {
1207
1221
  lastBaseBundlePathRef.current = existing;
1208
1222
  setBundlePath(existing);
1209
- const meta = await readJsonFile(toBundleMetaFileUri(key));
1223
+ const meta = embeddedMeta ?? await readJsonFile(toBundleMetaFileUri(key));
1210
1224
  if (meta == null ? void 0 : meta.fingerprint) {
1211
1225
  lastBaseFingerprintRef.current = meta.fingerprint;
1212
1226
  }
@@ -1793,11 +1807,28 @@ function hasNoOutcomeAfterLastHuman(messages) {
1793
1807
  }
1794
1808
 
1795
1809
  // src/studio/ui/RuntimeRenderer.tsx
1810
+ var React9 = __toESM(require("react"));
1796
1811
  var import_react_native6 = require("react-native");
1797
1812
  var import_runtime = require("@comergehq/runtime");
1798
1813
  var import_jsx_runtime3 = require("react/jsx-runtime");
1799
- function RuntimeRenderer({ appKey, bundlePath, forcePreparing, renderToken, style }) {
1814
+ function RuntimeRenderer({
1815
+ appKey,
1816
+ bundlePath,
1817
+ forcePreparing,
1818
+ renderToken,
1819
+ style,
1820
+ allowInitialPreparing = true
1821
+ }) {
1822
+ const [hasRenderedOnce, setHasRenderedOnce] = React9.useState(false);
1823
+ React9.useEffect(() => {
1824
+ if (bundlePath) {
1825
+ setHasRenderedOnce(true);
1826
+ }
1827
+ }, [bundlePath]);
1800
1828
  if (!bundlePath || forcePreparing) {
1829
+ if (!hasRenderedOnce && !forcePreparing && !allowInitialPreparing) {
1830
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native6.View, { style: [{ flex: 1 }, style] });
1831
+ }
1801
1832
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native6.View, { style: [{ flex: 1, justifyContent: "center", alignItems: "center", padding: 24 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { variant: "bodyMuted", children: "Preparing app\u2026" }) });
1802
1833
  }
1803
1834
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native6.View, { style: [{ flex: 1 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
@@ -1812,11 +1843,11 @@ function RuntimeRenderer({ appKey, bundlePath, forcePreparing, renderToken, styl
1812
1843
  }
1813
1844
 
1814
1845
  // src/studio/ui/StudioOverlay.tsx
1815
- var React41 = __toESM(require("react"));
1846
+ var React42 = __toESM(require("react"));
1816
1847
  var import_react_native54 = require("react-native");
1817
1848
 
1818
1849
  // src/components/studio-sheet/StudioBottomSheet.tsx
1819
- var React11 = __toESM(require("react"));
1850
+ var React12 = __toESM(require("react"));
1820
1851
  var import_react_native9 = require("react-native");
1821
1852
  var import_bottom_sheet = __toESM(require("@gorhom/bottom-sheet"));
1822
1853
  var import_react_native_safe_area_context = require("react-native-safe-area-context");
@@ -1826,20 +1857,20 @@ var import_react_native8 = require("react-native");
1826
1857
  var import_liquid_glass2 = require("@callstack/liquid-glass");
1827
1858
 
1828
1859
  // src/components/utils/ResettableLiquidGlassView.tsx
1829
- var React10 = __toESM(require("react"));
1860
+ var React11 = __toESM(require("react"));
1830
1861
  var import_liquid_glass = require("@callstack/liquid-glass");
1831
1862
 
1832
1863
  // src/components/utils/liquidGlassReset.tsx
1833
- var React9 = __toESM(require("react"));
1864
+ var React10 = __toESM(require("react"));
1834
1865
  var import_react_native7 = require("react-native");
1835
1866
  var import_jsx_runtime4 = require("react/jsx-runtime");
1836
- var LiquidGlassResetContext = React9.createContext(0);
1867
+ var LiquidGlassResetContext = React10.createContext(0);
1837
1868
  function LiquidGlassResetProvider({
1838
1869
  children,
1839
1870
  resetTriggers = []
1840
1871
  }) {
1841
- const [token, setToken] = React9.useState(0);
1842
- React9.useEffect(() => {
1872
+ const [token, setToken] = React10.useState(0);
1873
+ React10.useEffect(() => {
1843
1874
  if (import_react_native7.Platform.OS !== "ios") return;
1844
1875
  const onChange = (state) => {
1845
1876
  if (state === "active") setToken((t) => t + 1);
@@ -1847,21 +1878,21 @@ function LiquidGlassResetProvider({
1847
1878
  const sub = import_react_native7.AppState.addEventListener("change", onChange);
1848
1879
  return () => sub.remove();
1849
1880
  }, []);
1850
- React9.useEffect(() => {
1881
+ React10.useEffect(() => {
1851
1882
  setToken((t) => t + 1);
1852
1883
  }, resetTriggers);
1853
1884
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LiquidGlassResetContext.Provider, { value: token, children });
1854
1885
  }
1855
1886
  function useLiquidGlassResetToken() {
1856
- return React9.useContext(LiquidGlassResetContext);
1887
+ return React10.useContext(LiquidGlassResetContext);
1857
1888
  }
1858
1889
 
1859
1890
  // src/components/utils/ResettableLiquidGlassView.tsx
1860
1891
  var import_jsx_runtime5 = require("react/jsx-runtime");
1861
1892
  function ResettableLiquidGlassView({ children, ...props }) {
1862
1893
  const token = useLiquidGlassResetToken();
1863
- const [layoutBootKey, setLayoutBootKey] = React10.useState(0);
1864
- const sawNonZeroLayoutRef = React10.useRef(false);
1894
+ const [layoutBootKey, setLayoutBootKey] = React11.useState(0);
1895
+ const sawNonZeroLayoutRef = React11.useRef(false);
1865
1896
  const onLayout = (e) => {
1866
1897
  var _a;
1867
1898
  (_a = props.onLayout) == null ? void 0 : _a.call(props, e);
@@ -1935,11 +1966,11 @@ function StudioBottomSheet({
1935
1966
  }) {
1936
1967
  const theme = useTheme();
1937
1968
  const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
1938
- const internalSheetRef = React11.useRef(null);
1969
+ const internalSheetRef = React12.useRef(null);
1939
1970
  const resolvedSheetRef = sheetRef ?? internalSheetRef;
1940
- const currentIndexRef = React11.useRef(open ? snapPoints.length - 1 : -1);
1941
- const lastAppStateRef = React11.useRef(import_react_native9.AppState.currentState);
1942
- React11.useEffect(() => {
1971
+ const currentIndexRef = React12.useRef(open ? snapPoints.length - 1 : -1);
1972
+ const lastAppStateRef = React12.useRef(import_react_native9.AppState.currentState);
1973
+ React12.useEffect(() => {
1943
1974
  const sub = import_react_native9.AppState.addEventListener("change", (state) => {
1944
1975
  const prev = lastAppStateRef.current;
1945
1976
  lastAppStateRef.current = state;
@@ -1959,7 +1990,7 @@ function StudioBottomSheet({
1959
1990
  });
1960
1991
  return () => sub.remove();
1961
1992
  }, [open, resolvedSheetRef]);
1962
- React11.useEffect(() => {
1993
+ React12.useEffect(() => {
1963
1994
  const sheet = resolvedSheetRef.current;
1964
1995
  if (!sheet) return;
1965
1996
  if (open) {
@@ -1968,7 +1999,7 @@ function StudioBottomSheet({
1968
1999
  sheet.close();
1969
2000
  }
1970
2001
  }, [open, resolvedSheetRef, snapPoints.length]);
1971
- const handleChange = React11.useCallback(
2002
+ const handleChange = React12.useCallback(
1972
2003
  (index) => {
1973
2004
  currentIndexRef.current = index;
1974
2005
  onOpenChange == null ? void 0 : onOpenChange(index >= 0);
@@ -1997,12 +2028,12 @@ function StudioBottomSheet({
1997
2028
  }
1998
2029
 
1999
2030
  // src/components/studio-sheet/StudioSheetPager.tsx
2000
- var React12 = __toESM(require("react"));
2031
+ var React13 = __toESM(require("react"));
2001
2032
  var import_react_native10 = require("react-native");
2002
2033
  var import_jsx_runtime8 = require("react/jsx-runtime");
2003
2034
  function StudioSheetPager({ activePage, width, preview, chat, style }) {
2004
- const anim = React12.useRef(new import_react_native10.Animated.Value(activePage === "chat" ? 1 : 0)).current;
2005
- React12.useEffect(() => {
2035
+ const anim = React13.useRef(new import_react_native10.Animated.Value(activePage === "chat" ? 1 : 0)).current;
2036
+ React13.useEffect(() => {
2006
2037
  import_react_native10.Animated.spring(anim, {
2007
2038
  toValue: activePage === "chat" ? 1 : 0,
2008
2039
  useNativeDriver: true,
@@ -2050,14 +2081,14 @@ function StudioSheetPager({ activePage, width, preview, chat, style }) {
2050
2081
  ] });
2051
2082
  }
2052
2083
 
2053
- // src/components/floating-draggable-button/FloatingDraggableButton.tsx
2084
+ // src/components/bubble/Bubble.tsx
2054
2085
  var import_react = require("react");
2055
2086
  var import_react_native11 = require("react-native");
2056
2087
  var Haptics = __toESM(require("expo-haptics"));
2057
2088
  var import_react_native_reanimated = __toESM(require("react-native-reanimated"));
2058
2089
  var import_liquid_glass3 = require("@callstack/liquid-glass");
2059
2090
 
2060
- // src/components/floating-draggable-button/constants.ts
2091
+ // src/components/bubble/constants.ts
2061
2092
  var DEFAULT_SIZE = 48;
2062
2093
  var DEFAULT_EDGE_PADDING = 10;
2063
2094
  var DEFAULT_OFFSET = {
@@ -2068,7 +2099,7 @@ var ENTER_SCALE_FROM = 0.3;
2068
2099
  var ENTER_ROTATION_FROM_DEG = -180;
2069
2100
  var PULSE_DURATION_MS = 900;
2070
2101
 
2071
- // src/components/floating-draggable-button/FloatingDraggableButton.tsx
2102
+ // src/components/bubble/Bubble.tsx
2072
2103
  var import_jsx_runtime9 = require("react/jsx-runtime");
2073
2104
  var HIDDEN_OFFSET_X = 20;
2074
2105
  var SPRING_POSITION = { damping: 12, stiffness: 100, mass: 0.8 };
@@ -2090,7 +2121,7 @@ function getHiddenTranslateY(height) {
2090
2121
  function getFinalTranslateY(height, size, bottomOffset) {
2091
2122
  return height - size - bottomOffset;
2092
2123
  }
2093
- function FloatingDraggableButton({
2124
+ function Bubble({
2094
2125
  onPress,
2095
2126
  size = DEFAULT_SIZE,
2096
2127
  disabled = false,
@@ -2315,7 +2346,7 @@ var styles = import_react_native11.StyleSheet.create({
2315
2346
  });
2316
2347
 
2317
2348
  // src/components/overlays/EdgeGlowFrame.tsx
2318
- var React13 = __toESM(require("react"));
2349
+ var React14 = __toESM(require("react"));
2319
2350
  var import_react_native12 = require("react-native");
2320
2351
  var import_expo_linear_gradient = require("expo-linear-gradient");
2321
2352
 
@@ -2358,8 +2389,8 @@ function EdgeGlowFrame({
2358
2389
  }) {
2359
2390
  const theme = useTheme();
2360
2391
  const alpha = Math.max(0, Math.min(1, intensity));
2361
- const anim = React13.useRef(new import_react_native12.Animated.Value(visible ? 1 : 0)).current;
2362
- React13.useEffect(() => {
2392
+ const anim = React14.useRef(new import_react_native12.Animated.Value(visible ? 1 : 0)).current;
2393
+ React14.useEffect(() => {
2363
2394
  import_react_native12.Animated.timing(anim, {
2364
2395
  toValue: visible ? 1 : 0,
2365
2396
  duration: 300,
@@ -2410,12 +2441,12 @@ function EdgeGlowFrame({
2410
2441
  }
2411
2442
 
2412
2443
  // src/components/draw/DrawModeOverlay.tsx
2413
- var React16 = __toESM(require("react"));
2444
+ var React17 = __toESM(require("react"));
2414
2445
  var import_react_native16 = require("react-native");
2415
2446
  var import_react_native_view_shot = require("react-native-view-shot");
2416
2447
 
2417
2448
  // src/components/draw/DrawSurface.tsx
2418
- var React14 = __toESM(require("react"));
2449
+ var React15 = __toESM(require("react"));
2419
2450
  var import_react_native13 = require("react-native");
2420
2451
  var import_react_native_svg = __toESM(require("react-native-svg"));
2421
2452
 
@@ -2447,25 +2478,25 @@ function DrawSurface({
2447
2478
  style,
2448
2479
  minDistance = 1
2449
2480
  }) {
2450
- const [renderTick, setRenderTick] = React14.useState(0);
2451
- const currentPointsRef = React14.useRef([]);
2452
- const rafRef = React14.useRef(null);
2453
- const triggerRender = React14.useCallback(() => {
2481
+ const [renderTick, setRenderTick] = React15.useState(0);
2482
+ const currentPointsRef = React15.useRef([]);
2483
+ const rafRef = React15.useRef(null);
2484
+ const triggerRender = React15.useCallback(() => {
2454
2485
  if (rafRef.current !== null) return;
2455
2486
  rafRef.current = requestAnimationFrame(() => {
2456
2487
  rafRef.current = null;
2457
2488
  setRenderTick((n) => n + 1);
2458
2489
  });
2459
2490
  }, []);
2460
- React14.useEffect(() => () => {
2491
+ React15.useEffect(() => () => {
2461
2492
  if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
2462
2493
  }, []);
2463
- const onStart = React14.useCallback((e) => {
2494
+ const onStart = React15.useCallback((e) => {
2464
2495
  const { locationX, locationY } = e.nativeEvent;
2465
2496
  currentPointsRef.current = [{ x: locationX, y: locationY }];
2466
2497
  triggerRender();
2467
2498
  }, [triggerRender]);
2468
- const onMove = React14.useCallback((e, _g) => {
2499
+ const onMove = React15.useCallback((e, _g) => {
2469
2500
  const { locationX, locationY } = e.nativeEvent;
2470
2501
  const pts = currentPointsRef.current;
2471
2502
  if (pts.length > 0) {
@@ -2478,7 +2509,7 @@ function DrawSurface({
2478
2509
  currentPointsRef.current = [...pts, { x: locationX, y: locationY }];
2479
2510
  triggerRender();
2480
2511
  }, [minDistance, triggerRender]);
2481
- const onEnd = React14.useCallback(() => {
2512
+ const onEnd = React15.useCallback(() => {
2482
2513
  const points = currentPointsRef.current;
2483
2514
  if (points.length > 0) {
2484
2515
  onAddStroke({ points, color, width: strokeWidth });
@@ -2486,7 +2517,7 @@ function DrawSurface({
2486
2517
  currentPointsRef.current = [];
2487
2518
  triggerRender();
2488
2519
  }, [color, onAddStroke, strokeWidth, triggerRender]);
2489
- const panResponder = React14.useMemo(
2520
+ const panResponder = React15.useMemo(
2490
2521
  () => import_react_native13.PanResponder.create({
2491
2522
  onStartShouldSetPanResponder: () => true,
2492
2523
  onMoveShouldSetPanResponder: () => true,
@@ -2536,7 +2567,7 @@ var styles2 = import_react_native13.StyleSheet.create({
2536
2567
  });
2537
2568
 
2538
2569
  // src/components/draw/DrawToolbar.tsx
2539
- var React15 = __toESM(require("react"));
2570
+ var React16 = __toESM(require("react"));
2540
2571
  var import_react_native15 = require("react-native");
2541
2572
  var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
2542
2573
  var import_lucide_react_native = require("lucide-react-native");
@@ -2626,11 +2657,11 @@ function DrawToolbar({
2626
2657
  }) {
2627
2658
  const insets = (0, import_react_native_safe_area_context2.useSafeAreaInsets)();
2628
2659
  const { width: screenWidth, height: screenHeight } = (0, import_react_native15.useWindowDimensions)();
2629
- const [expanded, setExpanded] = React15.useState(false);
2630
- const pos = React15.useRef(new import_react_native15.Animated.ValueXY({ x: screenWidth / 2 - 110, y: -140 })).current;
2631
- const start = React15.useRef({ x: 0, y: 0 });
2632
- const currentPos = React15.useRef({ x: 0, y: 0 });
2633
- React15.useEffect(() => {
2660
+ const [expanded, setExpanded] = React16.useState(false);
2661
+ const pos = React16.useRef(new import_react_native15.Animated.ValueXY({ x: screenWidth / 2 - 110, y: -140 })).current;
2662
+ const start = React16.useRef({ x: 0, y: 0 });
2663
+ const currentPos = React16.useRef({ x: 0, y: 0 });
2664
+ React16.useEffect(() => {
2634
2665
  if (hidden) return;
2635
2666
  import_react_native15.Animated.spring(pos.y, {
2636
2667
  toValue: insets.top + 60,
@@ -2640,7 +2671,7 @@ function DrawToolbar({
2640
2671
  mass: 0.8
2641
2672
  }).start();
2642
2673
  }, [hidden, insets.top, pos.y]);
2643
- React15.useEffect(() => {
2674
+ React16.useEffect(() => {
2644
2675
  const id = pos.addListener((v) => {
2645
2676
  currentPos.current = { x: v.x ?? 0, y: v.y ?? 0 };
2646
2677
  });
@@ -2648,7 +2679,7 @@ function DrawToolbar({
2648
2679
  pos.removeListener(id);
2649
2680
  };
2650
2681
  }, [pos]);
2651
- const clamp2 = React15.useCallback(
2682
+ const clamp2 = React16.useCallback(
2652
2683
  (x, y) => {
2653
2684
  const minX = 10;
2654
2685
  const maxX = Math.max(10, screenWidth - 230);
@@ -2658,7 +2689,7 @@ function DrawToolbar({
2658
2689
  },
2659
2690
  [insets.top, screenHeight, screenWidth]
2660
2691
  );
2661
- const panResponder = React15.useMemo(
2692
+ const panResponder = React16.useMemo(
2662
2693
  () => import_react_native15.PanResponder.create({
2663
2694
  onStartShouldSetPanResponder: () => false,
2664
2695
  onMoveShouldSetPanResponder: (_e, g) => Math.abs(g.dx) > 5 || Math.abs(g.dy) > 5,
@@ -2686,7 +2717,7 @@ function DrawToolbar({
2686
2717
  children
2687
2718
  }) {
2688
2719
  const isDisabled = Boolean(disabled) || Boolean(capturingDisabled);
2689
- const [pressed, setPressed] = React15.useState(false);
2720
+ const [pressed, setPressed] = React16.useState(false);
2690
2721
  return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2691
2722
  import_react_native15.View,
2692
2723
  {
@@ -2824,7 +2855,7 @@ function DrawModeOverlay({
2824
2855
  renderDragHandle
2825
2856
  }) {
2826
2857
  const theme = useTheme();
2827
- const defaultPalette = React16.useMemo(
2858
+ const defaultPalette = React17.useMemo(
2828
2859
  () => [
2829
2860
  "#EF4444",
2830
2861
  // Red
@@ -2842,11 +2873,11 @@ function DrawModeOverlay({
2842
2873
  []
2843
2874
  );
2844
2875
  const colors = palette && palette.length > 0 ? palette : defaultPalette;
2845
- const [selectedColor, setSelectedColor] = React16.useState(colors[0] ?? "#EF4444");
2846
- const [strokes, setStrokes] = React16.useState([]);
2847
- const [capturing, setCapturing] = React16.useState(false);
2848
- const [hideUi, setHideUi] = React16.useState(false);
2849
- React16.useEffect(() => {
2876
+ const [selectedColor, setSelectedColor] = React17.useState(colors[0] ?? "#EF4444");
2877
+ const [strokes, setStrokes] = React17.useState([]);
2878
+ const [capturing, setCapturing] = React17.useState(false);
2879
+ const [hideUi, setHideUi] = React17.useState(false);
2880
+ React17.useEffect(() => {
2850
2881
  if (!visible) return;
2851
2882
  setStrokes([]);
2852
2883
  setSelectedColor(colors[0] ?? "#EF4444");
@@ -2854,14 +2885,14 @@ function DrawModeOverlay({
2854
2885
  setHideUi(false);
2855
2886
  }, [colors, visible]);
2856
2887
  const canUndo = strokes.length > 0;
2857
- const handleUndo = React16.useCallback(() => {
2888
+ const handleUndo = React17.useCallback(() => {
2858
2889
  setStrokes((prev) => prev.slice(0, -1));
2859
2890
  }, []);
2860
- const handleCancel = React16.useCallback(() => {
2891
+ const handleCancel = React17.useCallback(() => {
2861
2892
  setStrokes([]);
2862
2893
  onCancel();
2863
2894
  }, [onCancel]);
2864
- const handleDone = React16.useCallback(async () => {
2895
+ const handleDone = React17.useCallback(async () => {
2865
2896
  if (!captureTargetRef.current || capturing) return;
2866
2897
  try {
2867
2898
  setCapturing(true);
@@ -2921,7 +2952,7 @@ var styles3 = import_react_native16.StyleSheet.create({
2921
2952
  });
2922
2953
 
2923
2954
  // src/components/comments/AppCommentsSheet.tsx
2924
- var React23 = __toESM(require("react"));
2955
+ var React24 = __toESM(require("react"));
2925
2956
  var import_react_native22 = require("react-native");
2926
2957
  var import_bottom_sheet3 = require("@gorhom/bottom-sheet");
2927
2958
  var import_react_native_safe_area_context3 = require("react-native-safe-area-context");
@@ -2929,17 +2960,17 @@ var import_liquid_glass5 = require("@callstack/liquid-glass");
2929
2960
  var import_lucide_react_native4 = require("lucide-react-native");
2930
2961
 
2931
2962
  // src/components/chat/ChatComposer.tsx
2932
- var React18 = __toESM(require("react"));
2963
+ var React19 = __toESM(require("react"));
2933
2964
  var import_react_native18 = require("react-native");
2934
2965
  var import_liquid_glass4 = require("@callstack/liquid-glass");
2935
2966
  var import_lucide_react_native3 = require("lucide-react-native");
2936
2967
 
2937
2968
  // src/components/chat/MultilineTextInput.tsx
2938
- var React17 = __toESM(require("react"));
2969
+ var React18 = __toESM(require("react"));
2939
2970
  var import_react_native17 = require("react-native");
2940
2971
  var import_bottom_sheet2 = require("@gorhom/bottom-sheet");
2941
2972
  var import_jsx_runtime15 = require("react/jsx-runtime");
2942
- var MultilineTextInput = React17.forwardRef(function MultilineTextInput2({ useBottomSheetTextInput = false, placeholder, placeholderTextColor, style, ...props }, ref) {
2973
+ var MultilineTextInput = React18.forwardRef(function MultilineTextInput2({ useBottomSheetTextInput = false, placeholder, placeholderTextColor, style, ...props }, ref) {
2943
2974
  const theme = useTheme();
2944
2975
  const baseStyle = {
2945
2976
  minHeight: 44,
@@ -3011,7 +3042,7 @@ function AspectRatioThumbnail({
3011
3042
  onRemove,
3012
3043
  renderRemoveIcon
3013
3044
  }) {
3014
- const [aspectRatio, setAspectRatio] = React18.useState(1);
3045
+ const [aspectRatio, setAspectRatio] = React19.useState(1);
3015
3046
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react_native18.View, { style: { height: THUMBNAIL_HEIGHT, aspectRatio, position: "relative" }, children: [
3016
3047
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native18.View, { style: { flex: 1, borderRadius: 8, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3017
3048
  import_react_native18.Image,
@@ -3068,19 +3099,19 @@ function ChatComposer({
3068
3099
  style
3069
3100
  }) {
3070
3101
  const theme = useTheme();
3071
- const [internal, setInternal] = React18.useState("");
3102
+ const [internal, setInternal] = React19.useState("");
3072
3103
  const text = value ?? internal;
3073
3104
  const setText = onChangeValue ?? setInternal;
3074
3105
  const hasAttachments = attachments.length > 0;
3075
3106
  const hasText = text.trim().length > 0;
3076
3107
  const composerMinHeight = hasAttachments ? THUMBNAIL_HEIGHT + 44 + 24 : 44;
3077
3108
  const isButtonDisabled = sending || disabled || sendDisabled;
3078
- const maxInputHeight = React18.useMemo(() => import_react_native18.Dimensions.get("window").height * 0.5, []);
3079
- const shakeAnim = React18.useRef(new import_react_native18.Animated.Value(0)).current;
3080
- const [sendPressed, setSendPressed] = React18.useState(false);
3081
- const inputRef = React18.useRef(null);
3082
- const prevAutoFocusRef = React18.useRef(false);
3083
- React18.useEffect(() => {
3109
+ const maxInputHeight = React19.useMemo(() => import_react_native18.Dimensions.get("window").height * 0.5, []);
3110
+ const shakeAnim = React19.useRef(new import_react_native18.Animated.Value(0)).current;
3111
+ const [sendPressed, setSendPressed] = React19.useState(false);
3112
+ const inputRef = React19.useRef(null);
3113
+ const prevAutoFocusRef = React19.useRef(false);
3114
+ React19.useEffect(() => {
3084
3115
  const shouldFocus = autoFocus && !prevAutoFocusRef.current && !disabled && !sending;
3085
3116
  prevAutoFocusRef.current = autoFocus;
3086
3117
  if (!shouldFocus) return;
@@ -3090,7 +3121,7 @@ function ChatComposer({
3090
3121
  }, 75);
3091
3122
  return () => clearTimeout(t);
3092
3123
  }, [autoFocus, disabled, sending]);
3093
- const triggerShake = React18.useCallback(() => {
3124
+ const triggerShake = React19.useCallback(() => {
3094
3125
  shakeAnim.setValue(0);
3095
3126
  import_react_native18.Animated.sequence([
3096
3127
  import_react_native18.Animated.timing(shakeAnim, { toValue: 10, duration: 50, useNativeDriver: true }),
@@ -3100,7 +3131,7 @@ function ChatComposer({
3100
3131
  import_react_native18.Animated.timing(shakeAnim, { toValue: 0, duration: 50, useNativeDriver: true })
3101
3132
  ]).start();
3102
3133
  }, [shakeAnim]);
3103
- const handleSend = React18.useCallback(async () => {
3134
+ const handleSend = React19.useCallback(async () => {
3104
3135
  if (isButtonDisabled) return;
3105
3136
  if (!hasText) {
3106
3137
  triggerShake();
@@ -3233,7 +3264,7 @@ function ChatComposer({
3233
3264
  }
3234
3265
 
3235
3266
  // src/components/comments/CommentRow.tsx
3236
- var React19 = __toESM(require("react"));
3267
+ var React20 = __toESM(require("react"));
3237
3268
  var import_react_native20 = require("react-native");
3238
3269
 
3239
3270
  // src/components/primitives/Avatar.tsx
@@ -3305,9 +3336,9 @@ function formatTimeAgo(iso) {
3305
3336
  var import_jsx_runtime19 = require("react/jsx-runtime");
3306
3337
  function CommentRow({ comment, showDivider }) {
3307
3338
  const theme = useTheme();
3308
- const [authorName, setAuthorName] = React19.useState(null);
3309
- const [authorAvatar, setAuthorAvatar] = React19.useState(null);
3310
- React19.useEffect(() => {
3339
+ const [authorName, setAuthorName] = React20.useState(null);
3340
+ const [authorAvatar, setAuthorAvatar] = React20.useState(null);
3341
+ React20.useEffect(() => {
3311
3342
  let cancelled = false;
3312
3343
  (async () => {
3313
3344
  try {
@@ -3347,7 +3378,7 @@ function CommentRow({ comment, showDivider }) {
3347
3378
  }
3348
3379
 
3349
3380
  // src/components/comments/useAppComments.ts
3350
- var React20 = __toESM(require("react"));
3381
+ var React21 = __toESM(require("react"));
3351
3382
 
3352
3383
  // src/data/comments/remote.ts
3353
3384
  var AppCommentsRemoteDataSourceImpl = class extends BaseRemote {
@@ -3419,18 +3450,18 @@ var appCommentsRepository = new AppCommentsRepositoryImpl(appCommentsRemoteDataS
3419
3450
 
3420
3451
  // src/components/comments/useAppComments.ts
3421
3452
  function useAppComments(appId) {
3422
- const [comments, setComments] = React20.useState([]);
3423
- const [loading, setLoading] = React20.useState(false);
3424
- const [sending, setSending] = React20.useState(false);
3425
- const [error, setError] = React20.useState(null);
3426
- const sortByCreatedAtAsc = React20.useCallback((items) => {
3453
+ const [comments, setComments] = React21.useState([]);
3454
+ const [loading, setLoading] = React21.useState(false);
3455
+ const [sending, setSending] = React21.useState(false);
3456
+ const [error, setError] = React21.useState(null);
3457
+ const sortByCreatedAtAsc = React21.useCallback((items) => {
3427
3458
  return [...items].sort((a, b) => {
3428
3459
  const at = a.createdAt ? new Date(a.createdAt).getTime() : 0;
3429
3460
  const bt = b.createdAt ? new Date(b.createdAt).getTime() : 0;
3430
3461
  return at - bt;
3431
3462
  });
3432
3463
  }, []);
3433
- const refresh = React20.useCallback(async () => {
3464
+ const refresh = React21.useCallback(async () => {
3434
3465
  if (!appId) {
3435
3466
  setComments([]);
3436
3467
  return;
@@ -3447,10 +3478,10 @@ function useAppComments(appId) {
3447
3478
  setLoading(false);
3448
3479
  }
3449
3480
  }, [appId, sortByCreatedAtAsc]);
3450
- React20.useEffect(() => {
3481
+ React21.useEffect(() => {
3451
3482
  void refresh();
3452
3483
  }, [refresh]);
3453
- const create = React20.useCallback(
3484
+ const create = React21.useCallback(
3454
3485
  async (text) => {
3455
3486
  if (!appId) return;
3456
3487
  const trimmed = text.trim();
@@ -3473,11 +3504,11 @@ function useAppComments(appId) {
3473
3504
  }
3474
3505
 
3475
3506
  // src/components/comments/useAppDetails.ts
3476
- var React21 = __toESM(require("react"));
3507
+ var React22 = __toESM(require("react"));
3477
3508
  function useAppDetails(appId) {
3478
- const [app, setApp] = React21.useState(null);
3479
- const [loading, setLoading] = React21.useState(false);
3480
- React21.useEffect(() => {
3509
+ const [app, setApp] = React22.useState(null);
3510
+ const [loading, setLoading] = React22.useState(false);
3511
+ React22.useEffect(() => {
3481
3512
  if (!appId) {
3482
3513
  setApp(null);
3483
3514
  return;
@@ -3502,11 +3533,11 @@ function useAppDetails(appId) {
3502
3533
  }
3503
3534
 
3504
3535
  // src/components/comments/useIosKeyboardSnapFix.ts
3505
- var React22 = __toESM(require("react"));
3536
+ var React23 = __toESM(require("react"));
3506
3537
  var import_react_native21 = require("react-native");
3507
3538
  function useIosKeyboardSnapFix(sheetRef, options) {
3508
- const [keyboardVisible, setKeyboardVisible] = React22.useState(false);
3509
- React22.useEffect(() => {
3539
+ const [keyboardVisible, setKeyboardVisible] = React23.useState(false);
3540
+ React23.useEffect(() => {
3510
3541
  if (import_react_native21.Platform.OS !== "ios") return;
3511
3542
  const show = import_react_native21.Keyboard.addListener("keyboardWillShow", () => setKeyboardVisible(true));
3512
3543
  const hide = import_react_native21.Keyboard.addListener("keyboardWillHide", () => {
@@ -3534,16 +3565,16 @@ var import_jsx_runtime20 = require("react/jsx-runtime");
3534
3565
  function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3535
3566
  const theme = useTheme();
3536
3567
  const insets = (0, import_react_native_safe_area_context3.useSafeAreaInsets)();
3537
- const sheetRef = React23.useRef(null);
3538
- const snapPoints = React23.useMemo(() => ["50%", "90%"], []);
3539
- const currentIndexRef = React23.useRef(1);
3568
+ const sheetRef = React24.useRef(null);
3569
+ const snapPoints = React24.useMemo(() => ["50%", "90%"], []);
3570
+ const currentIndexRef = React24.useRef(1);
3540
3571
  const { comments, loading, sending, error, create, refresh } = useAppComments(appId);
3541
3572
  const { app, loading: loadingApp } = useAppDetails(appId);
3542
3573
  const { keyboardVisible } = useIosKeyboardSnapFix(sheetRef, {
3543
3574
  getCurrentIndex: () => currentIndexRef.current,
3544
3575
  targetIndex: 1
3545
3576
  });
3546
- React23.useEffect(() => {
3577
+ React24.useEffect(() => {
3547
3578
  var _a, _b;
3548
3579
  if (appId) {
3549
3580
  (_a = sheetRef.current) == null ? void 0 : _a.present();
@@ -3552,22 +3583,22 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3552
3583
  (_b = sheetRef.current) == null ? void 0 : _b.dismiss();
3553
3584
  }
3554
3585
  }, [appId, refresh]);
3555
- React23.useEffect(() => {
3586
+ React24.useEffect(() => {
3556
3587
  if (!appId) return;
3557
3588
  onCountChange == null ? void 0 : onCountChange(comments.length);
3558
3589
  }, [appId, comments.length, onCountChange]);
3559
- const renderBackdrop = React23.useCallback(
3590
+ const renderBackdrop = React24.useCallback(
3560
3591
  (props) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_bottom_sheet3.BottomSheetBackdrop, { ...props, disappearsOnIndex: -1, appearsOnIndex: 0, opacity: 0.5 }),
3561
3592
  []
3562
3593
  );
3563
- const handleChange = React23.useCallback(
3594
+ const handleChange = React24.useCallback(
3564
3595
  (index) => {
3565
3596
  currentIndexRef.current = index;
3566
3597
  if (index === -1) onClose();
3567
3598
  },
3568
3599
  [onClose]
3569
3600
  );
3570
- const handlePlay = React23.useCallback(async () => {
3601
+ const handlePlay = React24.useCallback(async () => {
3571
3602
  var _a;
3572
3603
  if (!appId) return;
3573
3604
  (_a = sheetRef.current) == null ? void 0 : _a.dismiss();
@@ -3777,7 +3808,7 @@ function StudioSheetHeader({ left, center, right, style }) {
3777
3808
  }
3778
3809
 
3779
3810
  // src/components/studio-sheet/StudioSheetHeaderIconButton.tsx
3780
- var React24 = __toESM(require("react"));
3811
+ var React25 = __toESM(require("react"));
3781
3812
  var import_react_native25 = require("react-native");
3782
3813
  var import_liquid_glass6 = require("@callstack/liquid-glass");
3783
3814
  var import_jsx_runtime23 = require("react/jsx-runtime");
@@ -3792,7 +3823,7 @@ function StudioSheetHeaderIconButton({
3792
3823
  }) {
3793
3824
  const theme = useTheme();
3794
3825
  const size = 44;
3795
- const [pressed, setPressed] = React24.useState(false);
3826
+ const [pressed, setPressed] = React25.useState(false);
3796
3827
  const solidBg = intent === "danger" ? theme.colors.danger : intent === "primary" ? theme.colors.primary : theme.colors.neutral;
3797
3828
  const glassFallbackBg = theme.scheme === "dark" ? "#18181B" : "#F6F6F6";
3798
3829
  const glassInnerBg = intent === "danger" ? theme.colors.danger : theme.colors.primary;
@@ -3982,14 +4013,14 @@ function PreviewHeroCard({
3982
4013
  }
3983
4014
 
3984
4015
  // src/components/preview/PreviewPlaceholder.tsx
3985
- var React25 = __toESM(require("react"));
4016
+ var React26 = __toESM(require("react"));
3986
4017
  var import_react_native29 = require("react-native");
3987
4018
  var import_expo_linear_gradient2 = require("expo-linear-gradient");
3988
4019
  var import_jsx_runtime28 = require("react/jsx-runtime");
3989
4020
  function PreviewPlaceholder({ visible, style }) {
3990
4021
  if (!visible) return null;
3991
- const opacityAnim = React25.useRef(new import_react_native29.Animated.Value(0)).current;
3992
- React25.useEffect(() => {
4022
+ const opacityAnim = React26.useRef(new import_react_native29.Animated.Value(0)).current;
4023
+ React26.useEffect(() => {
3993
4024
  if (!visible) return;
3994
4025
  const animation = import_react_native29.Animated.loop(
3995
4026
  import_react_native29.Animated.sequence([
@@ -4573,12 +4604,12 @@ function PreviewCustomizeSection({
4573
4604
  }
4574
4605
 
4575
4606
  // src/studio/ui/preview-panel/PreviewCollaborateSection.tsx
4576
- var React31 = __toESM(require("react"));
4607
+ var React32 = __toESM(require("react"));
4577
4608
  var import_react_native42 = require("react-native");
4578
4609
  var import_lucide_react_native9 = require("lucide-react-native");
4579
4610
 
4580
4611
  // src/components/merge-requests/MergeRequestStatusCard.tsx
4581
- var React27 = __toESM(require("react"));
4612
+ var React28 = __toESM(require("react"));
4582
4613
  var import_react_native38 = require("react-native");
4583
4614
  var import_lucide_react_native7 = require("lucide-react-native");
4584
4615
 
@@ -4660,11 +4691,11 @@ function toIsoString(input) {
4660
4691
  }
4661
4692
 
4662
4693
  // src/components/merge-requests/useControlledExpansion.ts
4663
- var React26 = __toESM(require("react"));
4694
+ var React27 = __toESM(require("react"));
4664
4695
  function useControlledExpansion(props) {
4665
- const [uncontrolled, setUncontrolled] = React26.useState(false);
4696
+ const [uncontrolled, setUncontrolled] = React27.useState(false);
4666
4697
  const expanded = props.expanded ?? uncontrolled;
4667
- const setExpanded = React26.useCallback(
4698
+ const setExpanded = React27.useCallback(
4668
4699
  (next) => {
4669
4700
  var _a;
4670
4701
  (_a = props.onExpandedChange) == null ? void 0 : _a.call(props, next);
@@ -4689,8 +4720,8 @@ function MergeRequestStatusCard({
4689
4720
  const isDark = theme.scheme === "dark";
4690
4721
  const textColor = isDark ? "#FFFFFF" : "#000000";
4691
4722
  const subTextColor = isDark ? "#A1A1AA" : "#71717A";
4692
- const status = React27.useMemo(() => getMergeRequestStatusDisplay(String(mergeRequest.status)), [mergeRequest.status]);
4693
- const { StatusIcon, iconColor, bgColor, statusText } = React27.useMemo(() => {
4723
+ const status = React28.useMemo(() => getMergeRequestStatusDisplay(String(mergeRequest.status)), [mergeRequest.status]);
4724
+ const { StatusIcon, iconColor, bgColor, statusText } = React28.useMemo(() => {
4694
4725
  switch (mergeRequest.status) {
4695
4726
  case "approved":
4696
4727
  case "merged":
@@ -4721,8 +4752,8 @@ function MergeRequestStatusCard({
4721
4752
  const createdIso = toIsoString(mergeRequest.createdAt ?? null);
4722
4753
  const headerTimeAgo = updatedIso ? formatTimeAgo(updatedIso) : "";
4723
4754
  const createdTimeAgo = createdIso ? formatTimeAgo(createdIso) : "";
4724
- const rotate = React27.useRef(new import_react_native38.Animated.Value(expanded ? 1 : 0)).current;
4725
- React27.useEffect(() => {
4755
+ const rotate = React28.useRef(new import_react_native38.Animated.Value(expanded ? 1 : 0)).current;
4756
+ React28.useEffect(() => {
4726
4757
  import_react_native38.Animated.timing(rotate, {
4727
4758
  toValue: expanded ? 1 : 0,
4728
4759
  duration: 200,
@@ -4813,16 +4844,16 @@ function MergeRequestStatusCard({
4813
4844
  }
4814
4845
 
4815
4846
  // src/components/merge-requests/ReviewMergeRequestCarousel.tsx
4816
- var React30 = __toESM(require("react"));
4847
+ var React31 = __toESM(require("react"));
4817
4848
  var import_react_native41 = require("react-native");
4818
4849
 
4819
4850
  // src/components/merge-requests/ReviewMergeRequestCard.tsx
4820
- var React29 = __toESM(require("react"));
4851
+ var React30 = __toESM(require("react"));
4821
4852
  var import_react_native40 = require("react-native");
4822
4853
  var import_lucide_react_native8 = require("lucide-react-native");
4823
4854
 
4824
4855
  // src/components/merge-requests/ReviewMergeRequestActionButton.tsx
4825
- var React28 = __toESM(require("react"));
4856
+ var React29 = __toESM(require("react"));
4826
4857
  var import_react_native39 = require("react-native");
4827
4858
  var import_jsx_runtime41 = require("react/jsx-runtime");
4828
4859
  function ReviewMergeRequestActionButton({
@@ -4833,7 +4864,7 @@ function ReviewMergeRequestActionButton({
4833
4864
  children,
4834
4865
  iconOnly
4835
4866
  }) {
4836
- const [pressed, setPressed] = React28.useState(false);
4867
+ const [pressed, setPressed] = React29.useState(false);
4837
4868
  const height = iconOnly ? 36 : 40;
4838
4869
  const width = iconOnly ? 36 : void 0;
4839
4870
  const paddingHorizontal = iconOnly ? 0 : 16;
@@ -4895,10 +4926,10 @@ function ReviewMergeRequestCard({
4895
4926
  onTest
4896
4927
  }) {
4897
4928
  const theme = useTheme();
4898
- const status = React29.useMemo(() => getMergeRequestStatusDisplay(mr.status), [mr.status]);
4929
+ const status = React30.useMemo(() => getMergeRequestStatusDisplay(mr.status), [mr.status]);
4899
4930
  const canAct = mr.status === "open";
4900
- const rotate = React29.useRef(new import_react_native40.Animated.Value(isExpanded ? 1 : 0)).current;
4901
- React29.useEffect(() => {
4931
+ const rotate = React30.useRef(new import_react_native40.Animated.Value(isExpanded ? 1 : 0)).current;
4932
+ React30.useEffect(() => {
4902
4933
  import_react_native40.Animated.timing(rotate, { toValue: isExpanded ? 1 : 0, duration: 200, useNativeDriver: true }).start();
4903
4934
  }, [isExpanded, rotate]);
4904
4935
  const position = total > 1 ? `${index + 1}/${total}` : "Merge request";
@@ -5030,11 +5061,11 @@ function ReviewMergeRequestCarousel({
5030
5061
  }) {
5031
5062
  const theme = useTheme();
5032
5063
  const { width } = (0, import_react_native41.useWindowDimensions)();
5033
- const [expanded, setExpanded] = React30.useState({});
5034
- const carouselScrollX = React30.useRef(new import_react_native41.Animated.Value(0)).current;
5064
+ const [expanded, setExpanded] = React31.useState({});
5065
+ const carouselScrollX = React31.useRef(new import_react_native41.Animated.Value(0)).current;
5035
5066
  const peekAmount = 24;
5036
5067
  const gap = 16;
5037
- const cardWidth = React30.useMemo(() => Math.max(1, width - theme.spacing.lg * 2 - peekAmount), [peekAmount, theme.spacing.lg, width]);
5068
+ const cardWidth = React31.useMemo(() => Math.max(1, width - theme.spacing.lg * 2 - peekAmount), [peekAmount, theme.spacing.lg, width]);
5038
5069
  const snapInterval = cardWidth + gap;
5039
5070
  const dotColor = theme.scheme === "dark" ? "#FFFFFF" : "#000000";
5040
5071
  if (mergeRequests.length === 0) return null;
@@ -5133,7 +5164,7 @@ function PreviewCollaborateSection({
5133
5164
  onTestMr
5134
5165
  }) {
5135
5166
  const theme = useTheme();
5136
- const [submittingMr, setSubmittingMr] = React31.useState(false);
5167
+ const [submittingMr, setSubmittingMr] = React32.useState(false);
5137
5168
  const hasSection = canSubmitMergeRequest || incomingMergeRequests.length > 0 || outgoingMergeRequests.length > 0;
5138
5169
  if (!hasSection) return null;
5139
5170
  const showActionsSubtitle = canSubmitMergeRequest && onSubmitMergeRequest || onTestMr && incomingMergeRequests.length > 0;
@@ -5241,7 +5272,7 @@ function PreviewCollaborateSection({
5241
5272
  }
5242
5273
 
5243
5274
  // src/studio/ui/preview-panel/usePreviewPanelData.ts
5244
- var React33 = __toESM(require("react"));
5275
+ var React34 = __toESM(require("react"));
5245
5276
 
5246
5277
  // src/data/apps/images/remote.ts
5247
5278
  var AppImagesRemoteDataSourceImpl = class extends BaseRemote {
@@ -5292,7 +5323,7 @@ var AppImagesRepositoryImpl = class extends BaseRepository {
5292
5323
  var appImagesRepository = new AppImagesRepositoryImpl(appImagesRemoteDataSource);
5293
5324
 
5294
5325
  // src/studio/hooks/useAppStats.ts
5295
- var React32 = __toESM(require("react"));
5326
+ var React33 = __toESM(require("react"));
5296
5327
  var Haptics2 = __toESM(require("expo-haptics"));
5297
5328
 
5298
5329
  // src/data/likes/remote.ts
@@ -5361,34 +5392,34 @@ function useAppStats({
5361
5392
  initialIsLiked = false,
5362
5393
  onOpenComments
5363
5394
  }) {
5364
- const [likeCount, setLikeCount] = React32.useState(initialLikes);
5365
- const [commentCount, setCommentCount] = React32.useState(initialComments);
5366
- const [forkCount, setForkCount] = React32.useState(initialForks);
5367
- const [isLiked, setIsLiked] = React32.useState(initialIsLiked);
5368
- const didMutateRef = React32.useRef(false);
5369
- const lastAppIdRef = React32.useRef("");
5370
- React32.useEffect(() => {
5395
+ const [likeCount, setLikeCount] = React33.useState(initialLikes);
5396
+ const [commentCount, setCommentCount] = React33.useState(initialComments);
5397
+ const [forkCount, setForkCount] = React33.useState(initialForks);
5398
+ const [isLiked, setIsLiked] = React33.useState(initialIsLiked);
5399
+ const didMutateRef = React33.useRef(false);
5400
+ const lastAppIdRef = React33.useRef("");
5401
+ React33.useEffect(() => {
5371
5402
  if (lastAppIdRef.current === appId) return;
5372
5403
  lastAppIdRef.current = appId;
5373
5404
  didMutateRef.current = false;
5374
5405
  }, [appId]);
5375
- React32.useEffect(() => {
5406
+ React33.useEffect(() => {
5376
5407
  if (didMutateRef.current) return;
5377
5408
  setLikeCount(initialLikes);
5378
5409
  }, [appId, initialLikes]);
5379
- React32.useEffect(() => {
5410
+ React33.useEffect(() => {
5380
5411
  if (didMutateRef.current) return;
5381
5412
  setCommentCount(initialComments);
5382
5413
  }, [appId, initialComments]);
5383
- React32.useEffect(() => {
5414
+ React33.useEffect(() => {
5384
5415
  if (didMutateRef.current) return;
5385
5416
  setForkCount(initialForks);
5386
5417
  }, [appId, initialForks]);
5387
- React32.useEffect(() => {
5418
+ React33.useEffect(() => {
5388
5419
  if (didMutateRef.current) return;
5389
5420
  setIsLiked(initialIsLiked);
5390
5421
  }, [appId, initialIsLiked]);
5391
- const handleLike = React32.useCallback(async () => {
5422
+ const handleLike = React33.useCallback(async () => {
5392
5423
  var _a, _b;
5393
5424
  if (!appId) return;
5394
5425
  didMutateRef.current = true;
@@ -5412,7 +5443,7 @@ function useAppStats({
5412
5443
  setLikeCount((prev) => Math.max(0, prev + (newIsLiked ? -1 : 1)));
5413
5444
  }
5414
5445
  }, [appId, isLiked, likeCount]);
5415
- const handleOpenComments = React32.useCallback(() => {
5446
+ const handleOpenComments = React33.useCallback(() => {
5416
5447
  if (!appId) return;
5417
5448
  try {
5418
5449
  void Haptics2.impactAsync(Haptics2.ImpactFeedbackStyle.Light);
@@ -5427,11 +5458,11 @@ function useAppStats({
5427
5458
  var LIKE_DEBUG_PREFIX = "[COMERGE_LIKE_DEBUG]";
5428
5459
  function usePreviewPanelData(params) {
5429
5460
  const { app, isOwner, outgoingMergeRequests, onOpenComments, commentCountOverride } = params;
5430
- const [imageUrl, setImageUrl] = React33.useState(null);
5431
- const [imageLoaded, setImageLoaded] = React33.useState(false);
5432
- const [insights, setInsights] = React33.useState({ likes: 0, comments: 0, forks: 0, downloads: 0 });
5433
- const [creator, setCreator] = React33.useState(null);
5434
- React33.useEffect(() => {
5461
+ const [imageUrl, setImageUrl] = React34.useState(null);
5462
+ const [imageLoaded, setImageLoaded] = React34.useState(false);
5463
+ const [insights, setInsights] = React34.useState({ likes: 0, comments: 0, forks: 0, downloads: 0 });
5464
+ const [creator, setCreator] = React34.useState(null);
5465
+ React34.useEffect(() => {
5435
5466
  if (!(app == null ? void 0 : app.id)) return;
5436
5467
  let cancelled = false;
5437
5468
  (async () => {
@@ -5446,7 +5477,7 @@ function usePreviewPanelData(params) {
5446
5477
  cancelled = true;
5447
5478
  };
5448
5479
  }, [app == null ? void 0 : app.id]);
5449
- React33.useEffect(() => {
5480
+ React34.useEffect(() => {
5450
5481
  if (!(app == null ? void 0 : app.createdBy)) return;
5451
5482
  let cancelled = false;
5452
5483
  (async () => {
@@ -5462,10 +5493,10 @@ function usePreviewPanelData(params) {
5462
5493
  cancelled = true;
5463
5494
  };
5464
5495
  }, [app == null ? void 0 : app.createdBy]);
5465
- React33.useEffect(() => {
5496
+ React34.useEffect(() => {
5466
5497
  setImageLoaded(false);
5467
5498
  }, [app == null ? void 0 : app.id]);
5468
- React33.useEffect(() => {
5499
+ React34.useEffect(() => {
5469
5500
  if (!(app == null ? void 0 : app.id)) return;
5470
5501
  let cancelled = false;
5471
5502
  (async () => {
@@ -5490,7 +5521,7 @@ function usePreviewPanelData(params) {
5490
5521
  cancelled = true;
5491
5522
  };
5492
5523
  }, [app == null ? void 0 : app.id]);
5493
- React33.useEffect(() => {
5524
+ React34.useEffect(() => {
5494
5525
  if (!(app == null ? void 0 : app.id)) return;
5495
5526
  log.debug(
5496
5527
  `${LIKE_DEBUG_PREFIX} usePreviewPanelData.appChanged appId=${app.id} app.isLiked=${String(app.isLiked)}`
@@ -5504,7 +5535,7 @@ function usePreviewPanelData(params) {
5504
5535
  initialIsLiked: Boolean(app == null ? void 0 : app.isLiked),
5505
5536
  onOpenComments
5506
5537
  });
5507
- const canSubmitMergeRequest = React33.useMemo(() => {
5538
+ const canSubmitMergeRequest = React34.useMemo(() => {
5508
5539
  if (!isOwner) return false;
5509
5540
  if (!app) return false;
5510
5541
  if (!app.forkedFromAppId) return false;
@@ -5617,16 +5648,16 @@ function PreviewPanel({
5617
5648
  }
5618
5649
 
5619
5650
  // src/studio/ui/ChatPanel.tsx
5620
- var React38 = __toESM(require("react"));
5651
+ var React39 = __toESM(require("react"));
5621
5652
  var import_react_native51 = require("react-native");
5622
5653
 
5623
5654
  // src/components/chat/ChatPage.tsx
5624
- var React36 = __toESM(require("react"));
5655
+ var React37 = __toESM(require("react"));
5625
5656
  var import_react_native47 = require("react-native");
5626
5657
  var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
5627
5658
 
5628
5659
  // src/components/chat/ChatMessageList.tsx
5629
- var React35 = __toESM(require("react"));
5660
+ var React36 = __toESM(require("react"));
5630
5661
  var import_react_native46 = require("react-native");
5631
5662
  var import_bottom_sheet5 = require("@gorhom/bottom-sheet");
5632
5663
 
@@ -5672,17 +5703,17 @@ function ChatMessageBubble({ message, renderContent, style }) {
5672
5703
  }
5673
5704
 
5674
5705
  // src/components/chat/TypingIndicator.tsx
5675
- var React34 = __toESM(require("react"));
5706
+ var React35 = __toESM(require("react"));
5676
5707
  var import_react_native45 = require("react-native");
5677
5708
  var import_jsx_runtime47 = require("react/jsx-runtime");
5678
5709
  function TypingIndicator({ style }) {
5679
5710
  const theme = useTheme();
5680
5711
  const dotColor = theme.colors.textSubtle;
5681
- const anims = React34.useMemo(
5712
+ const anims = React35.useMemo(
5682
5713
  () => [new import_react_native45.Animated.Value(0.3), new import_react_native45.Animated.Value(0.3), new import_react_native45.Animated.Value(0.3)],
5683
5714
  []
5684
5715
  );
5685
- React34.useEffect(() => {
5716
+ React35.useEffect(() => {
5686
5717
  const loops = [];
5687
5718
  anims.forEach((a, idx) => {
5688
5719
  const seq = import_react_native45.Animated.sequence([
@@ -5716,7 +5747,7 @@ function TypingIndicator({ style }) {
5716
5747
 
5717
5748
  // src/components/chat/ChatMessageList.tsx
5718
5749
  var import_jsx_runtime48 = require("react/jsx-runtime");
5719
- var ChatMessageList = React35.forwardRef(
5750
+ var ChatMessageList = React36.forwardRef(
5720
5751
  ({
5721
5752
  messages,
5722
5753
  showTypingIndicator = false,
@@ -5727,20 +5758,20 @@ var ChatMessageList = React35.forwardRef(
5727
5758
  nearBottomThreshold = 200
5728
5759
  }, ref) => {
5729
5760
  const theme = useTheme();
5730
- const listRef = React35.useRef(null);
5731
- const nearBottomRef = React35.useRef(true);
5732
- const initialScrollDoneRef = React35.useRef(false);
5733
- const lastMessageIdRef = React35.useRef(null);
5734
- const data = React35.useMemo(() => {
5761
+ const listRef = React36.useRef(null);
5762
+ const nearBottomRef = React36.useRef(true);
5763
+ const initialScrollDoneRef = React36.useRef(false);
5764
+ const lastMessageIdRef = React36.useRef(null);
5765
+ const data = React36.useMemo(() => {
5735
5766
  return [...messages].reverse();
5736
5767
  }, [messages]);
5737
- const scrollToBottom = React35.useCallback((options) => {
5768
+ const scrollToBottom = React36.useCallback((options) => {
5738
5769
  var _a;
5739
5770
  const animated = (options == null ? void 0 : options.animated) ?? true;
5740
5771
  (_a = listRef.current) == null ? void 0 : _a.scrollToOffset({ offset: 0, animated });
5741
5772
  }, []);
5742
- React35.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
5743
- const handleScroll = React35.useCallback(
5773
+ React36.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
5774
+ const handleScroll = React36.useCallback(
5744
5775
  (e) => {
5745
5776
  const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;
5746
5777
  const distanceFromBottom = Math.max(contentOffset.y - Math.max(bottomInset, 0), 0);
@@ -5752,7 +5783,7 @@ var ChatMessageList = React35.forwardRef(
5752
5783
  },
5753
5784
  [bottomInset, nearBottomThreshold, onNearBottomChange]
5754
5785
  );
5755
- React35.useEffect(() => {
5786
+ React36.useEffect(() => {
5756
5787
  if (!initialScrollDoneRef.current) return;
5757
5788
  const lastId = messages.length > 0 ? messages[messages.length - 1].id : null;
5758
5789
  const prevLastId = lastMessageIdRef.current;
@@ -5762,7 +5793,7 @@ var ChatMessageList = React35.forwardRef(
5762
5793
  const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
5763
5794
  return () => cancelAnimationFrame(id);
5764
5795
  }, [messages, scrollToBottom]);
5765
- React35.useEffect(() => {
5796
+ React36.useEffect(() => {
5766
5797
  if (showTypingIndicator && nearBottomRef.current) {
5767
5798
  const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
5768
5799
  return () => cancelAnimationFrame(id);
@@ -5824,9 +5855,9 @@ function ChatPage({
5824
5855
  }) {
5825
5856
  const theme = useTheme();
5826
5857
  const insets = (0, import_react_native_safe_area_context4.useSafeAreaInsets)();
5827
- const [composerHeight, setComposerHeight] = React36.useState(0);
5828
- const [keyboardVisible, setKeyboardVisible] = React36.useState(false);
5829
- React36.useEffect(() => {
5858
+ const [composerHeight, setComposerHeight] = React37.useState(0);
5859
+ const [keyboardVisible, setKeyboardVisible] = React37.useState(false);
5860
+ React37.useEffect(() => {
5830
5861
  if (import_react_native47.Platform.OS !== "ios") return;
5831
5862
  const show = import_react_native47.Keyboard.addListener("keyboardWillShow", () => setKeyboardVisible(true));
5832
5863
  const hide = import_react_native47.Keyboard.addListener("keyboardWillHide", () => setKeyboardVisible(false));
@@ -5838,12 +5869,12 @@ function ChatPage({
5838
5869
  const footerBottomPadding = import_react_native47.Platform.OS === "ios" ? keyboardVisible ? 0 : insets.bottom : insets.bottom + 10;
5839
5870
  const overlayBottom = composerHeight + footerBottomPadding + theme.spacing.lg;
5840
5871
  const bottomInset = composerHeight + footerBottomPadding + theme.spacing.xl;
5841
- const resolvedOverlay = React36.useMemo(() => {
5872
+ const resolvedOverlay = React37.useMemo(() => {
5842
5873
  var _a;
5843
5874
  if (!overlay) return null;
5844
- if (!React36.isValidElement(overlay)) return overlay;
5875
+ if (!React37.isValidElement(overlay)) return overlay;
5845
5876
  const prevStyle = (_a = overlay.props) == null ? void 0 : _a.style;
5846
- return React36.cloneElement(overlay, {
5877
+ return React37.cloneElement(overlay, {
5847
5878
  style: [prevStyle, { bottom: overlayBottom }]
5848
5879
  });
5849
5880
  }, [overlay, overlayBottom]);
@@ -5898,15 +5929,15 @@ function ChatPage({
5898
5929
  }
5899
5930
 
5900
5931
  // src/components/chat/ScrollToBottomButton.tsx
5901
- var React37 = __toESM(require("react"));
5932
+ var React38 = __toESM(require("react"));
5902
5933
  var import_react_native48 = require("react-native");
5903
5934
  var import_react_native_reanimated2 = __toESM(require("react-native-reanimated"));
5904
5935
  var import_jsx_runtime50 = require("react/jsx-runtime");
5905
5936
  function ScrollToBottomButton({ visible, onPress, children, style }) {
5906
5937
  const theme = useTheme();
5907
5938
  const progress = (0, import_react_native_reanimated2.useSharedValue)(visible ? 1 : 0);
5908
- const [pressed, setPressed] = React37.useState(false);
5909
- React37.useEffect(() => {
5939
+ const [pressed, setPressed] = React38.useState(false);
5940
+ React38.useEffect(() => {
5910
5941
  progress.value = (0, import_react_native_reanimated2.withTiming)(visible ? 1 : 0, { duration: 200, easing: import_react_native_reanimated2.Easing.out(import_react_native_reanimated2.Easing.ease) });
5911
5942
  }, [progress, visible]);
5912
5943
  const animStyle = (0, import_react_native_reanimated2.useAnimatedStyle)(() => ({
@@ -6061,9 +6092,9 @@ function ChatPanel({
6061
6092
  onStartDraw,
6062
6093
  onSend
6063
6094
  }) {
6064
- const listRef = React38.useRef(null);
6065
- const [nearBottom, setNearBottom] = React38.useState(true);
6066
- const handleSend = React38.useCallback(
6095
+ const listRef = React39.useRef(null);
6096
+ const [nearBottom, setNearBottom] = React39.useState(true);
6097
+ const handleSend = React39.useCallback(
6067
6098
  async (text, composerAttachments) => {
6068
6099
  const all = composerAttachments ?? attachments;
6069
6100
  await onSend(text, all.length > 0 ? all : void 0);
@@ -6077,7 +6108,7 @@ function ChatPanel({
6077
6108
  },
6078
6109
  [attachments, nearBottom, onClearAttachments, onSend]
6079
6110
  );
6080
- const handleScrollToBottom = React38.useCallback(() => {
6111
+ const handleScrollToBottom = React39.useCallback(() => {
6081
6112
  var _a;
6082
6113
  (_a = listRef.current) == null ? void 0 : _a.scrollToBottom({ animated: true });
6083
6114
  }, []);
@@ -6151,7 +6182,7 @@ function ChatPanel({
6151
6182
  }
6152
6183
 
6153
6184
  // src/components/dialogs/ConfirmMergeRequestDialog.tsx
6154
- var React39 = __toESM(require("react"));
6185
+ var React40 = __toESM(require("react"));
6155
6186
  var import_react_native53 = require("react-native");
6156
6187
 
6157
6188
  // src/components/primitives/Modal.tsx
@@ -6199,14 +6230,14 @@ function ConfirmMergeRequestDialog({
6199
6230
  onTestFirst
6200
6231
  }) {
6201
6232
  const theme = useTheme();
6202
- const close = React39.useCallback(() => onOpenChange(false), [onOpenChange]);
6233
+ const close = React40.useCallback(() => onOpenChange(false), [onOpenChange]);
6203
6234
  const canConfirm = Boolean(mergeRequest) && !approveDisabled;
6204
- const handleConfirm = React39.useCallback(() => {
6235
+ const handleConfirm = React40.useCallback(() => {
6205
6236
  if (!mergeRequest) return;
6206
6237
  onOpenChange(false);
6207
6238
  void onConfirm();
6208
6239
  }, [mergeRequest, onConfirm, onOpenChange]);
6209
- const handleTestFirst = React39.useCallback(() => {
6240
+ const handleTestFirst = React40.useCallback(() => {
6210
6241
  if (!mergeRequest) return;
6211
6242
  onOpenChange(false);
6212
6243
  void onTestFirst(mergeRequest);
@@ -6355,7 +6386,7 @@ function ConfirmMergeFlow({
6355
6386
  }
6356
6387
 
6357
6388
  // src/studio/hooks/useOptimisticChatMessages.ts
6358
- var React40 = __toESM(require("react"));
6389
+ var React41 = __toESM(require("react"));
6359
6390
  function makeOptimisticId() {
6360
6391
  return `optimistic:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 10)}`;
6361
6392
  }
@@ -6393,11 +6424,11 @@ function useOptimisticChatMessages({
6393
6424
  chatMessages,
6394
6425
  onSendChat
6395
6426
  }) {
6396
- const [optimisticChat, setOptimisticChat] = React40.useState([]);
6397
- React40.useEffect(() => {
6427
+ const [optimisticChat, setOptimisticChat] = React41.useState([]);
6428
+ React41.useEffect(() => {
6398
6429
  setOptimisticChat([]);
6399
6430
  }, [threadId]);
6400
- const messages = React40.useMemo(() => {
6431
+ const messages = React41.useMemo(() => {
6401
6432
  if (!optimisticChat || optimisticChat.length === 0) return chatMessages;
6402
6433
  const unresolved = optimisticChat.filter((o) => !isOptimisticResolvedByServer(chatMessages, o));
6403
6434
  if (unresolved.length === 0) return chatMessages;
@@ -6413,7 +6444,7 @@ function useOptimisticChatMessages({
6413
6444
  merged.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
6414
6445
  return merged;
6415
6446
  }, [chatMessages, optimisticChat]);
6416
- React40.useEffect(() => {
6447
+ React41.useEffect(() => {
6417
6448
  if (optimisticChat.length === 0) return;
6418
6449
  setOptimisticChat((prev) => {
6419
6450
  if (prev.length === 0) return prev;
@@ -6421,7 +6452,7 @@ function useOptimisticChatMessages({
6421
6452
  return next.length === prev.length ? prev : next;
6422
6453
  });
6423
6454
  }, [chatMessages, optimisticChat.length]);
6424
- const onSend = React40.useCallback(
6455
+ const onSend = React41.useCallback(
6425
6456
  async (text, attachments) => {
6426
6457
  if (shouldForkOnEdit) {
6427
6458
  await onSendChat(text, attachments);
@@ -6470,18 +6501,18 @@ function StudioOverlay({
6470
6501
  chatShowTypingIndicator,
6471
6502
  onSendChat,
6472
6503
  onNavigateHome,
6473
- showFloatingButton,
6504
+ showBubble,
6474
6505
  studioControlOptions
6475
6506
  }) {
6476
6507
  const theme = useTheme();
6477
6508
  const { width } = (0, import_react_native54.useWindowDimensions)();
6478
- const [sheetOpen, setSheetOpen] = React41.useState(false);
6479
- const sheetOpenRef = React41.useRef(sheetOpen);
6480
- const [activePage, setActivePage] = React41.useState("preview");
6481
- const [drawing, setDrawing] = React41.useState(false);
6482
- const [chatAttachments, setChatAttachments] = React41.useState([]);
6483
- const [commentsAppId, setCommentsAppId] = React41.useState(null);
6484
- const [commentsCount, setCommentsCount] = React41.useState(null);
6509
+ const [sheetOpen, setSheetOpen] = React42.useState(false);
6510
+ const sheetOpenRef = React42.useRef(sheetOpen);
6511
+ const [activePage, setActivePage] = React42.useState("preview");
6512
+ const [drawing, setDrawing] = React42.useState(false);
6513
+ const [chatAttachments, setChatAttachments] = React42.useState([]);
6514
+ const [commentsAppId, setCommentsAppId] = React42.useState(null);
6515
+ const [commentsCount, setCommentsCount] = React42.useState(null);
6485
6516
  const threadId = (app == null ? void 0 : app.threadId) ?? null;
6486
6517
  const optimistic = useOptimisticChatMessages({
6487
6518
  threadId,
@@ -6489,24 +6520,24 @@ function StudioOverlay({
6489
6520
  chatMessages,
6490
6521
  onSendChat
6491
6522
  });
6492
- const [confirmMrId, setConfirmMrId] = React41.useState(null);
6493
- const confirmMr = React41.useMemo(
6523
+ const [confirmMrId, setConfirmMrId] = React42.useState(null);
6524
+ const confirmMr = React42.useMemo(
6494
6525
  () => confirmMrId ? incomingMergeRequests.find((m) => m.id === confirmMrId) ?? null : null,
6495
6526
  [confirmMrId, incomingMergeRequests]
6496
6527
  );
6497
- const handleSheetOpenChange = React41.useCallback((open) => {
6528
+ const handleSheetOpenChange = React42.useCallback((open) => {
6498
6529
  setSheetOpen(open);
6499
6530
  if (!open) import_react_native54.Keyboard.dismiss();
6500
6531
  }, []);
6501
- const closeSheet = React41.useCallback(() => {
6532
+ const closeSheet = React42.useCallback(() => {
6502
6533
  handleSheetOpenChange(false);
6503
6534
  }, [handleSheetOpenChange]);
6504
- const openSheet = React41.useCallback(() => setSheetOpen(true), []);
6505
- const goToChat = React41.useCallback(() => {
6535
+ const openSheet = React42.useCallback(() => setSheetOpen(true), []);
6536
+ const goToChat = React42.useCallback(() => {
6506
6537
  setActivePage("chat");
6507
6538
  openSheet();
6508
6539
  }, [openSheet]);
6509
- const backToPreview = React41.useCallback(() => {
6540
+ const backToPreview = React42.useCallback(() => {
6510
6541
  if (import_react_native54.Platform.OS !== "ios") {
6511
6542
  import_react_native54.Keyboard.dismiss();
6512
6543
  setActivePage("preview");
@@ -6524,11 +6555,11 @@ function StudioOverlay({
6524
6555
  const t = setTimeout(finalize, 350);
6525
6556
  import_react_native54.Keyboard.dismiss();
6526
6557
  }, []);
6527
- const startDraw = React41.useCallback(() => {
6558
+ const startDraw = React42.useCallback(() => {
6528
6559
  setDrawing(true);
6529
6560
  closeSheet();
6530
6561
  }, [closeSheet]);
6531
- const handleDrawCapture = React41.useCallback(
6562
+ const handleDrawCapture = React42.useCallback(
6532
6563
  (dataUrl) => {
6533
6564
  setChatAttachments((prev) => [...prev, dataUrl]);
6534
6565
  setDrawing(false);
@@ -6537,7 +6568,7 @@ function StudioOverlay({
6537
6568
  },
6538
6569
  [openSheet]
6539
6570
  );
6540
- const toggleSheet = React41.useCallback(async () => {
6571
+ const toggleSheet = React42.useCallback(async () => {
6541
6572
  if (!sheetOpen) {
6542
6573
  const shouldExitTest = Boolean(testingMrId) || isTesting;
6543
6574
  if (shouldExitTest) {
@@ -6549,7 +6580,7 @@ function StudioOverlay({
6549
6580
  closeSheet();
6550
6581
  }
6551
6582
  }, [closeSheet, isTesting, onRestoreBase, sheetOpen, testingMrId]);
6552
- const handleTestMr = React41.useCallback(
6583
+ const handleTestMr = React42.useCallback(
6553
6584
  async (mr) => {
6554
6585
  if (!onTestMr) return;
6555
6586
  await onTestMr(mr);
@@ -6557,10 +6588,10 @@ function StudioOverlay({
6557
6588
  },
6558
6589
  [closeSheet, onTestMr]
6559
6590
  );
6560
- React41.useEffect(() => {
6591
+ React42.useEffect(() => {
6561
6592
  sheetOpenRef.current = sheetOpen;
6562
6593
  }, [sheetOpen]);
6563
- React41.useEffect(() => {
6594
+ React42.useEffect(() => {
6564
6595
  const poller = (0, import_studio_control.startStudioControlPolling)((action) => {
6565
6596
  if (action === "show" && !sheetOpenRef.current) openSheet();
6566
6597
  if (action === "hide" && sheetOpenRef.current) closeSheet();
@@ -6568,7 +6599,7 @@ function StudioOverlay({
6568
6599
  }, studioControlOptions);
6569
6600
  return () => poller.stop();
6570
6601
  }, [closeSheet, openSheet, studioControlOptions, toggleSheet]);
6571
- React41.useEffect(() => {
6602
+ React42.useEffect(() => {
6572
6603
  void (0, import_studio_control.publishComergeStudioUIState)(sheetOpen, studioControlOptions);
6573
6604
  }, [sheetOpen, studioControlOptions]);
6574
6605
  return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
@@ -6627,8 +6658,8 @@ function StudioOverlay({
6627
6658
  )
6628
6659
  }
6629
6660
  ) }),
6630
- showFloatingButton && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
6631
- FloatingDraggableButton,
6661
+ showBubble && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
6662
+ Bubble,
6632
6663
  {
6633
6664
  visible: !sheetOpen && !drawing,
6634
6665
  ariaLabel: sheetOpen ? "Hide studio" : "Show studio",
@@ -6676,24 +6707,25 @@ function StudioOverlay({
6676
6707
  var import_jsx_runtime58 = require("react/jsx-runtime");
6677
6708
  function ComergeStudio({
6678
6709
  appId,
6679
- apiKey,
6710
+ clientKey: clientKey2,
6680
6711
  appKey = "MicroMain",
6681
6712
  onNavigateHome,
6682
6713
  style,
6683
- showFloatingButton = true,
6684
- studioControlOptions
6714
+ showBubble = true,
6715
+ studioControlOptions,
6716
+ embeddedBaseBundles
6685
6717
  }) {
6686
- const [activeAppId, setActiveAppId] = React42.useState(appId);
6687
- const [runtimeAppId, setRuntimeAppId] = React42.useState(appId);
6688
- const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React42.useState(null);
6689
- const platform = React42.useMemo(() => import_react_native55.Platform.OS === "ios" ? "ios" : "android", []);
6690
- React42.useEffect(() => {
6718
+ const [activeAppId, setActiveAppId] = React43.useState(appId);
6719
+ const [runtimeAppId, setRuntimeAppId] = React43.useState(appId);
6720
+ const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React43.useState(null);
6721
+ const platform = React43.useMemo(() => import_react_native55.Platform.OS === "ios" ? "ios" : "android", []);
6722
+ React43.useEffect(() => {
6691
6723
  setActiveAppId(appId);
6692
6724
  setRuntimeAppId(appId);
6693
6725
  setPendingRuntimeTargetAppId(null);
6694
6726
  }, [appId]);
6695
- const captureTargetRef = React42.useRef(null);
6696
- return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(StudioBootstrap, { apiKey, children: ({ userId }) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_bottom_sheet6.BottomSheetModalProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(LiquidGlassResetProvider, { resetTriggers: [appId, activeAppId, runtimeAppId], children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
6727
+ const captureTargetRef = React43.useRef(null);
6728
+ return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(StudioBootstrap, { clientKey: clientKey2, fallback: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_react_native55.View, { style: { flex: 1 } }), children: ({ userId }) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_bottom_sheet6.BottomSheetModalProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(LiquidGlassResetProvider, { resetTriggers: [appId, activeAppId, runtimeAppId], children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
6697
6729
  ComergeStudioInner,
6698
6730
  {
6699
6731
  userId,
@@ -6708,8 +6740,9 @@ function ComergeStudio({
6708
6740
  onNavigateHome,
6709
6741
  captureTargetRef,
6710
6742
  style,
6711
- showFloatingButton,
6712
- studioControlOptions
6743
+ showBubble,
6744
+ studioControlOptions,
6745
+ embeddedBaseBundles
6713
6746
  }
6714
6747
  ) }) }) });
6715
6748
  }
@@ -6726,17 +6759,18 @@ function ComergeStudioInner({
6726
6759
  onNavigateHome,
6727
6760
  captureTargetRef,
6728
6761
  style,
6729
- showFloatingButton,
6730
- studioControlOptions
6762
+ showBubble,
6763
+ studioControlOptions,
6764
+ embeddedBaseBundles
6731
6765
  }) {
6732
6766
  const { app, loading: appLoading } = useApp(activeAppId);
6733
6767
  const { app: runtimeAppFromHook } = useApp(runtimeAppId, { enabled: runtimeAppId !== activeAppId });
6734
6768
  const runtimeApp = runtimeAppId === activeAppId ? app : runtimeAppFromHook;
6735
- const sawEditingOnPendingTargetRef = React42.useRef(false);
6736
- React42.useEffect(() => {
6769
+ const sawEditingOnPendingTargetRef = React43.useRef(false);
6770
+ React43.useEffect(() => {
6737
6771
  sawEditingOnPendingTargetRef.current = false;
6738
6772
  }, [pendingRuntimeTargetAppId]);
6739
- React42.useEffect(() => {
6773
+ React43.useEffect(() => {
6740
6774
  if (!pendingRuntimeTargetAppId) return;
6741
6775
  if (activeAppId !== pendingRuntimeTargetAppId) return;
6742
6776
  if ((app == null ? void 0 : app.status) === "editing") {
@@ -6751,15 +6785,16 @@ function ComergeStudioInner({
6751
6785
  const bundle = useBundleManager({
6752
6786
  base: { appId: runtimeAppId, commitId: (runtimeApp == null ? void 0 : runtimeApp.headCommitId) ?? void 0 },
6753
6787
  platform,
6754
- canRequestLatest: (runtimeApp == null ? void 0 : runtimeApp.status) === "ready"
6788
+ canRequestLatest: (runtimeApp == null ? void 0 : runtimeApp.status) === "ready",
6789
+ embeddedBaseBundles
6755
6790
  });
6756
- const sawEditingOnActiveAppRef = React42.useRef(false);
6757
- const [showPostEditPreparing, setShowPostEditPreparing] = React42.useState(false);
6758
- React42.useEffect(() => {
6791
+ const sawEditingOnActiveAppRef = React43.useRef(false);
6792
+ const [showPostEditPreparing, setShowPostEditPreparing] = React43.useState(false);
6793
+ React43.useEffect(() => {
6759
6794
  sawEditingOnActiveAppRef.current = false;
6760
6795
  setShowPostEditPreparing(false);
6761
6796
  }, [activeAppId]);
6762
- React42.useEffect(() => {
6797
+ React43.useEffect(() => {
6763
6798
  if (!(app == null ? void 0 : app.id)) return;
6764
6799
  if (app.status === "editing") {
6765
6800
  sawEditingOnActiveAppRef.current = true;
@@ -6771,7 +6806,7 @@ function ComergeStudioInner({
6771
6806
  sawEditingOnActiveAppRef.current = false;
6772
6807
  }
6773
6808
  }, [app == null ? void 0 : app.id, app == null ? void 0 : app.status]);
6774
- React42.useEffect(() => {
6809
+ React43.useEffect(() => {
6775
6810
  if (!showPostEditPreparing) return;
6776
6811
  const stillProcessingBaseBundle = bundle.loading && bundle.loadingMode === "base" && !bundle.isTesting;
6777
6812
  if (!stillProcessingBaseBundle) {
@@ -6781,10 +6816,10 @@ function ComergeStudioInner({
6781
6816
  const threadId = (app == null ? void 0 : app.threadId) ?? "";
6782
6817
  const thread = useThreadMessages(threadId);
6783
6818
  const mergeRequests = useMergeRequests({ appId: activeAppId });
6784
- const hasOpenOutgoingMr = React42.useMemo(() => {
6819
+ const hasOpenOutgoingMr = React43.useMemo(() => {
6785
6820
  return mergeRequests.lists.outgoing.some((mr) => mr.status === "open");
6786
6821
  }, [mergeRequests.lists.outgoing]);
6787
- const incomingReviewMrs = React42.useMemo(() => {
6822
+ const incomingReviewMrs = React43.useMemo(() => {
6788
6823
  if (!userId) return mergeRequests.lists.incoming;
6789
6824
  return mergeRequests.lists.incoming.filter((mr) => mr.createdBy !== userId);
6790
6825
  }, [mergeRequests.lists.incoming, userId]);
@@ -6806,9 +6841,9 @@ function ComergeStudioInner({
6806
6841
  uploadAttachments: uploader.uploadBase64Images
6807
6842
  });
6808
6843
  const chatSendDisabled = hasNoOutcomeAfterLastHuman(thread.raw);
6809
- const [processingMrId, setProcessingMrId] = React42.useState(null);
6810
- const [testingMrId, setTestingMrId] = React42.useState(null);
6811
- const chatShowTypingIndicator = React42.useMemo(() => {
6844
+ const [processingMrId, setProcessingMrId] = React43.useState(null);
6845
+ const [testingMrId, setTestingMrId] = React43.useState(null);
6846
+ const chatShowTypingIndicator = React43.useMemo(() => {
6812
6847
  var _a;
6813
6848
  if (!thread.raw || thread.raw.length === 0) return false;
6814
6849
  const last = thread.raw[thread.raw.length - 1];
@@ -6822,7 +6857,8 @@ function ComergeStudioInner({
6822
6857
  appKey,
6823
6858
  bundlePath: bundle.bundlePath,
6824
6859
  forcePreparing: showPostEditPreparing,
6825
- renderToken: bundle.renderToken
6860
+ renderToken: bundle.renderToken,
6861
+ allowInitialPreparing: !embeddedBaseBundles
6826
6862
  }
6827
6863
  ),
6828
6864
  /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
@@ -6878,7 +6914,7 @@ function ComergeStudioInner({
6878
6914
  chatShowTypingIndicator,
6879
6915
  onSendChat: (text, attachments) => actions.sendEdit({ prompt: text, attachments }),
6880
6916
  onNavigateHome,
6881
- showFloatingButton,
6917
+ showBubble,
6882
6918
  studioControlOptions
6883
6919
  }
6884
6920
  )