@comergehq/studio 0.1.8 → 0.1.10

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,8 +36,8 @@ __export(index_exports, {
36
36
  module.exports = __toCommonJS(index_exports);
37
37
 
38
38
  // src/studio/ComergeStudio.tsx
39
- var React39 = __toESM(require("react"));
40
- var import_react_native53 = require("react-native");
39
+ var React40 = __toESM(require("react"));
40
+ var import_react_native54 = require("react-native");
41
41
  var import_bottom_sheet6 = require("@gorhom/bottom-sheet");
42
42
 
43
43
  // src/studio/bootstrap/StudioBootstrap.tsx
@@ -982,6 +982,39 @@ var BundlesRepositoryImpl = class extends BaseRepository {
982
982
  var bundlesRepository = new BundlesRepositoryImpl(bundlesRemoteDataSource);
983
983
 
984
984
  // src/studio/hooks/useBundleManager.ts
985
+ function sleep(ms) {
986
+ return new Promise((r) => setTimeout(r, ms));
987
+ }
988
+ function isRetryableNetworkError(e) {
989
+ var _a;
990
+ const err = e;
991
+ const code = typeof (err == null ? void 0 : err.code) === "string" ? err.code : "";
992
+ const message = typeof (err == null ? void 0 : err.message) === "string" ? err.message : "";
993
+ if (code === "ERR_NETWORK" || code === "ECONNABORTED") return true;
994
+ if (message.toLowerCase().includes("network error")) return true;
995
+ if (message.toLowerCase().includes("timeout")) return true;
996
+ const status = typeof ((_a = err == null ? void 0 : err.response) == null ? void 0 : _a.status) === "number" ? err.response.status : void 0;
997
+ if (status && (status === 429 || status >= 500)) return true;
998
+ return false;
999
+ }
1000
+ async function withRetry(fn, opts) {
1001
+ let lastErr = null;
1002
+ for (let attempt = 1; attempt <= opts.attempts; attempt += 1) {
1003
+ try {
1004
+ return await fn();
1005
+ } catch (e) {
1006
+ lastErr = e;
1007
+ const retryable = isRetryableNetworkError(e);
1008
+ if (!retryable || attempt >= opts.attempts) {
1009
+ throw e;
1010
+ }
1011
+ const exp = Math.min(opts.maxDelayMs, opts.baseDelayMs * Math.pow(2, attempt - 1));
1012
+ const jitter = Math.floor(Math.random() * 250);
1013
+ await sleep(exp + jitter);
1014
+ }
1015
+ }
1016
+ throw lastErr;
1017
+ }
985
1018
  function safeName(s) {
986
1019
  return s.replace(/[^a-zA-Z0-9._-]/g, "_");
987
1020
  }
@@ -1039,8 +1072,16 @@ async function getExistingNonEmptyFileUri(fileUri) {
1039
1072
  async function downloadIfMissing(url, fileUri) {
1040
1073
  const existing = await getExistingNonEmptyFileUri(fileUri);
1041
1074
  if (existing) return existing;
1042
- const res = await FileSystem.downloadAsync(url, fileUri);
1043
- return res.uri;
1075
+ return await withRetry(
1076
+ async () => {
1077
+ await deleteFileIfExists(fileUri);
1078
+ const res = await FileSystem.downloadAsync(url, fileUri);
1079
+ const ok = await getExistingNonEmptyFileUri(res.uri);
1080
+ if (!ok) throw new Error("Downloaded bundle is empty.");
1081
+ return res.uri;
1082
+ },
1083
+ { attempts: 3, baseDelayMs: 500, maxDelayMs: 4e3 }
1084
+ );
1044
1085
  }
1045
1086
  async function deleteFileIfExists(fileUri) {
1046
1087
  try {
@@ -1054,11 +1095,15 @@ async function deleteFileIfExists(fileUri) {
1054
1095
  async function safeReplaceFileFromUrl(url, targetUri, tmpKey) {
1055
1096
  const tmpUri = toBundleFileUri(`tmp:${tmpKey}:${Date.now()}`);
1056
1097
  try {
1057
- await FileSystem.downloadAsync(url, tmpUri);
1058
- const tmpOk = await getExistingNonEmptyFileUri(tmpUri);
1059
- if (!tmpOk) {
1060
- throw new Error("Downloaded bundle is empty.");
1061
- }
1098
+ await withRetry(
1099
+ async () => {
1100
+ await deleteFileIfExists(tmpUri);
1101
+ await FileSystem.downloadAsync(url, tmpUri);
1102
+ const tmpOk = await getExistingNonEmptyFileUri(tmpUri);
1103
+ if (!tmpOk) throw new Error("Downloaded bundle is empty.");
1104
+ },
1105
+ { attempts: 3, baseDelayMs: 500, maxDelayMs: 4e3 }
1106
+ );
1062
1107
  await deleteFileIfExists(targetUri);
1063
1108
  await FileSystem.moveAsync({ from: tmpUri, to: targetUri });
1064
1109
  const finalOk = await getExistingNonEmptyFileUri(targetUri);
@@ -1071,28 +1116,44 @@ async function safeReplaceFileFromUrl(url, targetUri, tmpKey) {
1071
1116
  async function pollBundle(appId, bundleId, opts) {
1072
1117
  const start = Date.now();
1073
1118
  while (true) {
1074
- const bundle = await bundlesRepository.getById(appId, bundleId);
1075
- if (bundle.status === "succeeded" || bundle.status === "failed") return bundle;
1119
+ try {
1120
+ const bundle = await bundlesRepository.getById(appId, bundleId);
1121
+ if (bundle.status === "succeeded" || bundle.status === "failed") return bundle;
1122
+ } catch (e) {
1123
+ if (!isRetryableNetworkError(e)) {
1124
+ throw e;
1125
+ }
1126
+ }
1076
1127
  if (Date.now() - start > opts.timeoutMs) {
1077
1128
  throw new Error("Bundle build timed out.");
1078
1129
  }
1079
- await new Promise((r) => setTimeout(r, opts.intervalMs));
1130
+ await sleep(opts.intervalMs);
1080
1131
  }
1081
1132
  }
1082
1133
  async function resolveBundlePath(src, platform, mode) {
1083
1134
  const { appId, commitId } = src;
1084
1135
  const dir = bundlesCacheDir();
1085
1136
  await ensureDir(dir);
1086
- const initiate = await bundlesRepository.initiate(appId, {
1087
- platform,
1088
- commitId: commitId ?? void 0,
1089
- idempotencyKey: `${appId}:${commitId ?? "head"}:${platform}`
1090
- });
1137
+ const initiate = await withRetry(
1138
+ async () => {
1139
+ return await bundlesRepository.initiate(appId, {
1140
+ platform,
1141
+ commitId: commitId ?? void 0,
1142
+ idempotencyKey: `${appId}:${commitId ?? "head"}:${platform}`
1143
+ });
1144
+ },
1145
+ { attempts: 3, baseDelayMs: 500, maxDelayMs: 4e3 }
1146
+ );
1091
1147
  const finalBundle = initiate.status === "succeeded" || initiate.status === "failed" ? initiate : await pollBundle(appId, initiate.id, { timeoutMs: 3 * 60 * 1e3, intervalMs: 1200 });
1092
1148
  if (finalBundle.status === "failed") {
1093
1149
  throw new Error("Bundle build failed.");
1094
1150
  }
1095
- const signed = await bundlesRepository.getSignedDownloadUrl(appId, finalBundle.id, { redirect: false });
1151
+ const signed = await withRetry(
1152
+ async () => {
1153
+ return await bundlesRepository.getSignedDownloadUrl(appId, finalBundle.id, { redirect: false });
1154
+ },
1155
+ { attempts: 3, baseDelayMs: 500, maxDelayMs: 4e3 }
1156
+ );
1096
1157
  const bundlePath = mode === "base" ? await safeReplaceFileFromUrl(
1097
1158
  signed.url,
1098
1159
  toBundleFileUri(baseBundleKey(appId, platform)),
@@ -1108,6 +1169,7 @@ function useBundleManager({
1108
1169
  const [bundlePath, setBundlePath] = React5.useState(null);
1109
1170
  const [renderToken, setRenderToken] = React5.useState(0);
1110
1171
  const [loading, setLoading] = React5.useState(false);
1172
+ const [loadingMode, setLoadingMode] = React5.useState(null);
1111
1173
  const [statusLabel, setStatusLabel] = React5.useState(null);
1112
1174
  const [error, setError] = React5.useState(null);
1113
1175
  const [isTesting, setIsTesting] = React5.useState(false);
@@ -1123,6 +1185,7 @@ function useBundleManager({
1123
1185
  baseOpIdRef.current += 1;
1124
1186
  if (activeLoadModeRef.current === "base") {
1125
1187
  setLoading(false);
1188
+ setLoadingMode(null);
1126
1189
  setStatusLabel(null);
1127
1190
  activeLoadModeRef.current = null;
1128
1191
  }
@@ -1187,6 +1250,7 @@ function useBundleManager({
1187
1250
  const opId = mode === "base" ? ++baseOpIdRef.current : ++testOpIdRef.current;
1188
1251
  activeLoadModeRef.current = mode;
1189
1252
  setLoading(true);
1253
+ setLoadingMode(mode);
1190
1254
  setError(null);
1191
1255
  setStatusLabel(mode === "test" ? "Loading test bundle\u2026" : "Loading latest build\u2026");
1192
1256
  if (mode === "base") {
@@ -1229,6 +1293,7 @@ function useBundleManager({
1229
1293
  if (mode === "base" && opId !== baseOpIdRef.current) return;
1230
1294
  if (mode === "test" && opId !== testOpIdRef.current) return;
1231
1295
  setLoading(false);
1296
+ setLoadingMode(null);
1232
1297
  if (activeLoadModeRef.current === mode) activeLoadModeRef.current = null;
1233
1298
  }
1234
1299
  }, [activateCachedBase, platform]);
@@ -1250,7 +1315,7 @@ function useBundleManager({
1250
1315
  if (!canRequestLatest) return;
1251
1316
  void loadBase();
1252
1317
  }, [base.appId, base.commitId, platform, canRequestLatest, loadBase]);
1253
- return { bundlePath, renderToken, loading, statusLabel, error, isTesting, loadBase, loadTest, restoreBase };
1318
+ return { bundlePath, renderToken, loading, loadingMode, statusLabel, error, isTesting, loadBase, loadTest, restoreBase };
1254
1319
  }
1255
1320
 
1256
1321
  // src/studio/hooks/useMergeRequests.ts
@@ -1509,6 +1574,8 @@ function useMergeRequests(params) {
1509
1574
 
1510
1575
  // src/studio/hooks/useAttachmentUpload.ts
1511
1576
  var React7 = __toESM(require("react"));
1577
+ var import_react_native5 = require("react-native");
1578
+ var FileSystem2 = __toESM(require("expo-file-system/legacy"));
1512
1579
 
1513
1580
  // src/data/attachment/remote.ts
1514
1581
  var AttachmentRemoteDataSourceImpl = class extends BaseRemote {
@@ -1548,6 +1615,40 @@ var attachmentRepository = new AttachmentRepositoryImpl(
1548
1615
  );
1549
1616
 
1550
1617
  // src/studio/hooks/useAttachmentUpload.ts
1618
+ async function dataUrlToBlobAndroid(dataUrl) {
1619
+ const normalized = dataUrl.startsWith("data:") ? dataUrl : `data:image/png;base64,${dataUrl}`;
1620
+ const comma = normalized.indexOf(",");
1621
+ if (comma === -1) {
1622
+ throw new Error("Invalid data URL (missing comma separator)");
1623
+ }
1624
+ const header = normalized.slice(0, comma);
1625
+ const base64 = normalized.slice(comma + 1);
1626
+ const mimeMatch = header.match(/data:(.*?);base64/i);
1627
+ const mimeType = (mimeMatch == null ? void 0 : mimeMatch[1]) ?? "application/octet-stream";
1628
+ const cacheDir = FileSystem2.cacheDirectory;
1629
+ if (!cacheDir) {
1630
+ throw new Error("expo-file-system cacheDirectory is unavailable");
1631
+ }
1632
+ const fileUri = `${cacheDir}attachment-${Date.now()}-${Math.random().toString(16).slice(2)}.bin`;
1633
+ await FileSystem2.writeAsStringAsync(fileUri, base64, {
1634
+ encoding: FileSystem2.EncodingType.Base64
1635
+ });
1636
+ try {
1637
+ const resp = await fetch(fileUri);
1638
+ const blob = await resp.blob();
1639
+ return blob.type ? blob : new Blob([blob], { type: mimeType });
1640
+ } finally {
1641
+ void FileSystem2.deleteAsync(fileUri, { idempotent: true }).catch(() => {
1642
+ });
1643
+ }
1644
+ }
1645
+ function getMimeTypeFromDataUrl(dataUrl) {
1646
+ const normalized = dataUrl.startsWith("data:") ? dataUrl : `data:image/png;base64,${dataUrl}`;
1647
+ const comma = normalized.indexOf(",");
1648
+ const header = comma === -1 ? normalized : normalized.slice(0, comma);
1649
+ const mimeMatch = header.match(/data:(.*?);base64/i);
1650
+ return (mimeMatch == null ? void 0 : mimeMatch[1]) ?? "image/png";
1651
+ }
1551
1652
  function useAttachmentUpload() {
1552
1653
  const [uploading, setUploading] = React7.useState(false);
1553
1654
  const [error, setError] = React7.useState(null);
@@ -1560,15 +1661,15 @@ function useAttachmentUpload() {
1560
1661
  const blobs = await Promise.all(
1561
1662
  dataUrls.map(async (dataUrl, idx) => {
1562
1663
  const normalized = dataUrl.startsWith("data:") ? dataUrl : `data:image/png;base64,${dataUrl}`;
1563
- const resp = await fetch(normalized);
1564
- const blob = await resp.blob();
1565
- return { blob, idx };
1664
+ const blob = import_react_native5.Platform.OS === "android" ? await dataUrlToBlobAndroid(normalized) : await (await fetch(normalized)).blob();
1665
+ const mimeType = getMimeTypeFromDataUrl(normalized);
1666
+ return { blob, idx, mimeType };
1566
1667
  })
1567
1668
  );
1568
- const files = blobs.map(({ blob }, idx) => ({
1669
+ const files = blobs.map(({ blob, mimeType }, idx) => ({
1569
1670
  name: `attachment-${Date.now()}-${idx}.png`,
1570
1671
  size: blob.size,
1571
- mimeType: blob.type || "image/png"
1672
+ mimeType
1572
1673
  }));
1573
1674
  const presign = await attachmentRepository.presign({ threadId, appId, files });
1574
1675
  await Promise.all(presign.uploads.map((u, index) => attachmentRepository.upload(u, blobs[index].blob)));
@@ -1692,14 +1793,14 @@ function hasNoOutcomeAfterLastHuman(messages) {
1692
1793
  }
1693
1794
 
1694
1795
  // src/studio/ui/RuntimeRenderer.tsx
1695
- var import_react_native5 = require("react-native");
1796
+ var import_react_native6 = require("react-native");
1696
1797
  var import_runtime = require("@comergehq/runtime");
1697
1798
  var import_jsx_runtime3 = require("react/jsx-runtime");
1698
- function RuntimeRenderer({ appKey, bundlePath, renderToken, style }) {
1699
- if (!bundlePath) {
1700
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.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" }) });
1799
+ function RuntimeRenderer({ appKey, bundlePath, forcePreparing, renderToken, style }) {
1800
+ if (!bundlePath || forcePreparing) {
1801
+ 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" }) });
1701
1802
  }
1702
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native5.View, { style: [{ flex: 1 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1803
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_react_native6.View, { style: [{ flex: 1 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1703
1804
  import_runtime.ComergeRuntimeRenderer,
1704
1805
  {
1705
1806
  appKey,
@@ -1711,17 +1812,17 @@ function RuntimeRenderer({ appKey, bundlePath, renderToken, style }) {
1711
1812
  }
1712
1813
 
1713
1814
  // src/studio/ui/StudioOverlay.tsx
1714
- var React38 = __toESM(require("react"));
1715
- var import_react_native52 = require("react-native");
1815
+ var React39 = __toESM(require("react"));
1816
+ var import_react_native53 = require("react-native");
1716
1817
 
1717
1818
  // src/components/studio-sheet/StudioBottomSheet.tsx
1718
1819
  var React9 = __toESM(require("react"));
1719
- var import_react_native7 = require("react-native");
1820
+ var import_react_native8 = require("react-native");
1720
1821
  var import_bottom_sheet = __toESM(require("@gorhom/bottom-sheet"));
1721
1822
  var import_react_native_safe_area_context = require("react-native-safe-area-context");
1722
1823
 
1723
1824
  // src/components/studio-sheet/StudioSheetBackground.tsx
1724
- var import_react_native6 = require("react-native");
1825
+ var import_react_native7 = require("react-native");
1725
1826
  var import_liquid_glass = require("@callstack/liquid-glass");
1726
1827
  var import_jsx_runtime4 = require("react/jsx-runtime");
1727
1828
  function StudioSheetBackground({
@@ -1729,7 +1830,7 @@ function StudioSheetBackground({
1729
1830
  renderBackground
1730
1831
  }) {
1731
1832
  const theme = useTheme();
1732
- const radius = import_react_native6.Platform.OS === "ios" ? 39 : 16;
1833
+ const radius = import_react_native7.Platform.OS === "ios" ? 39 : 16;
1733
1834
  const fallbackBgColor = theme.scheme === "dark" ? "rgba(11, 8, 15, 0.85)" : "rgba(255, 255, 255, 0.85)";
1734
1835
  const secondaryBgBaseColor = theme.scheme === "dark" ? "rgb(24, 24, 27)" : "rgb(173, 173, 173)";
1735
1836
  const containerStyle = {
@@ -1750,7 +1851,7 @@ function StudioSheetBackground({
1750
1851
  }
1751
1852
  ),
1752
1853
  import_liquid_glass.isLiquidGlassSupported && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1753
- import_react_native6.View,
1854
+ import_react_native7.View,
1754
1855
  {
1755
1856
  style: [
1756
1857
  containerStyle,
@@ -1775,7 +1876,7 @@ var import_jsx_runtime5 = require("react/jsx-runtime");
1775
1876
  function StudioBottomSheet({
1776
1877
  open,
1777
1878
  onOpenChange,
1778
- snapPoints = ["80%", "100%"],
1879
+ snapPoints = ["100%"],
1779
1880
  sheetRef,
1780
1881
  background,
1781
1882
  children,
@@ -1786,13 +1887,13 @@ function StudioBottomSheet({
1786
1887
  const internalSheetRef = React9.useRef(null);
1787
1888
  const resolvedSheetRef = sheetRef ?? internalSheetRef;
1788
1889
  const currentIndexRef = React9.useRef(open ? snapPoints.length - 1 : -1);
1789
- const lastAppStateRef = React9.useRef(import_react_native7.AppState.currentState);
1890
+ const lastAppStateRef = React9.useRef(import_react_native8.AppState.currentState);
1790
1891
  React9.useEffect(() => {
1791
- const sub = import_react_native7.AppState.addEventListener("change", (state) => {
1892
+ const sub = import_react_native8.AppState.addEventListener("change", (state) => {
1792
1893
  const prev = lastAppStateRef.current;
1793
1894
  lastAppStateRef.current = state;
1794
1895
  if (state === "background" || state === "inactive") {
1795
- import_react_native7.Keyboard.dismiss();
1896
+ import_react_native8.Keyboard.dismiss();
1796
1897
  return;
1797
1898
  }
1798
1899
  if (state !== "active") return;
@@ -1800,25 +1901,13 @@ function StudioBottomSheet({
1800
1901
  if (!sheet) return;
1801
1902
  const idx = currentIndexRef.current;
1802
1903
  if (open && idx >= 0) {
1803
- import_react_native7.Keyboard.dismiss();
1904
+ import_react_native8.Keyboard.dismiss();
1804
1905
  requestAnimationFrame(() => sheet.snapToIndex(idx));
1805
1906
  setTimeout(() => sheet.snapToIndex(idx), 120);
1806
1907
  }
1807
1908
  });
1808
1909
  return () => sub.remove();
1809
1910
  }, [open, resolvedSheetRef]);
1810
- React9.useEffect(() => {
1811
- if (import_react_native7.Platform.OS !== "ios") return;
1812
- const sub = import_react_native7.Keyboard.addListener("keyboardDidHide", () => {
1813
- const sheet = resolvedSheetRef.current;
1814
- if (!sheet || !open) return;
1815
- const targetIndex = snapPoints.length - 1;
1816
- if (currentIndexRef.current === targetIndex) {
1817
- setTimeout(() => sheet.snapToIndex(targetIndex), 10);
1818
- }
1819
- });
1820
- return () => sub.remove();
1821
- }, [open, resolvedSheetRef, snapPoints.length]);
1822
1911
  React9.useEffect(() => {
1823
1912
  const sheet = resolvedSheetRef.current;
1824
1913
  if (!sheet) return;
@@ -1841,9 +1930,9 @@ function StudioBottomSheet({
1841
1930
  ref: resolvedSheetRef,
1842
1931
  index: open ? snapPoints.length - 1 : -1,
1843
1932
  snapPoints,
1933
+ enableDynamicSizing: false,
1844
1934
  enablePanDownToClose: true,
1845
- keyboardBehavior: "interactive",
1846
- keyboardBlurBehavior: "restore",
1935
+ enableContentPanningGesture: false,
1847
1936
  android_keyboardInputMode: "adjustResize",
1848
1937
  backgroundComponent: (props) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StudioSheetBackground, { ...props, renderBackground: background == null ? void 0 : background.renderBackground }),
1849
1938
  topInset: insets.top,
@@ -1851,19 +1940,19 @@ function StudioBottomSheet({
1851
1940
  handleIndicatorStyle: { backgroundColor: theme.colors.handleIndicator },
1852
1941
  onChange: handleChange,
1853
1942
  ...bottomSheetProps,
1854
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.View, { style: { flex: 1, overflow: "hidden" }, children })
1943
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native8.View, { style: { flex: 1, overflow: "hidden" }, children })
1855
1944
  }
1856
1945
  );
1857
1946
  }
1858
1947
 
1859
1948
  // src/components/studio-sheet/StudioSheetPager.tsx
1860
1949
  var React10 = __toESM(require("react"));
1861
- var import_react_native8 = require("react-native");
1950
+ var import_react_native9 = require("react-native");
1862
1951
  var import_jsx_runtime6 = require("react/jsx-runtime");
1863
1952
  function StudioSheetPager({ activePage, width, preview, chat, style }) {
1864
- const anim = React10.useRef(new import_react_native8.Animated.Value(activePage === "chat" ? 1 : 0)).current;
1953
+ const anim = React10.useRef(new import_react_native9.Animated.Value(activePage === "chat" ? 1 : 0)).current;
1865
1954
  React10.useEffect(() => {
1866
- import_react_native8.Animated.spring(anim, {
1955
+ import_react_native9.Animated.spring(anim, {
1867
1956
  toValue: activePage === "chat" ? 1 : 0,
1868
1957
  useNativeDriver: true,
1869
1958
  tension: 65,
@@ -1872,9 +1961,9 @@ function StudioSheetPager({ activePage, width, preview, chat, style }) {
1872
1961
  }, [activePage, anim]);
1873
1962
  const previewTranslateX = anim.interpolate({ inputRange: [0, 1], outputRange: [0, -width] });
1874
1963
  const chatTranslateX = anim.interpolate({ inputRange: [0, 1], outputRange: [width, 0] });
1875
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native8.Animated.View, { style: [{ flex: 1 }, style], children: [
1964
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_react_native9.Animated.View, { style: [{ flex: 1 }, style], children: [
1876
1965
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1877
- import_react_native8.Animated.View,
1966
+ import_react_native9.Animated.View,
1878
1967
  {
1879
1968
  style: [
1880
1969
  {
@@ -1891,7 +1980,7 @@ function StudioSheetPager({ activePage, width, preview, chat, style }) {
1891
1980
  }
1892
1981
  ),
1893
1982
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1894
- import_react_native8.Animated.View,
1983
+ import_react_native9.Animated.View,
1895
1984
  {
1896
1985
  style: [
1897
1986
  {
@@ -1912,7 +2001,7 @@ function StudioSheetPager({ activePage, width, preview, chat, style }) {
1912
2001
 
1913
2002
  // src/components/floating-draggable-button/FloatingDraggableButton.tsx
1914
2003
  var import_react = require("react");
1915
- var import_react_native9 = require("react-native");
2004
+ var import_react_native10 = require("react-native");
1916
2005
  var Haptics = __toESM(require("expo-haptics"));
1917
2006
  var import_react_native_reanimated = __toESM(require("react-native-reanimated"));
1918
2007
  var import_liquid_glass2 = require("@callstack/liquid-glass");
@@ -1971,7 +2060,7 @@ function FloatingDraggableButton({
1971
2060
  backgroundColor
1972
2061
  }) {
1973
2062
  const theme = useTheme();
1974
- const { width, height } = (0, import_react_native9.useWindowDimensions)();
2063
+ const { width, height } = (0, import_react_native10.useWindowDimensions)();
1975
2064
  const isDanger = variant === "danger";
1976
2065
  const onPressRef = (0, import_react.useRef)(onPress);
1977
2066
  (0, import_react.useEffect)(() => {
@@ -2074,7 +2163,7 @@ function FloatingDraggableButton({
2074
2163
  }
2075
2164
  }, [forceShowTrigger, visible, animateIn]);
2076
2165
  const panResponder = (0, import_react.useRef)(
2077
- import_react_native9.PanResponder.create({
2166
+ import_react_native10.PanResponder.create({
2078
2167
  onStartShouldSetPanResponder: () => true,
2079
2168
  onMoveShouldSetPanResponder: () => true,
2080
2169
  onPanResponderGrant: () => {
@@ -2137,24 +2226,24 @@ function FloatingDraggableButton({
2137
2226
  interactive: true,
2138
2227
  effect: "clear",
2139
2228
  children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2140
- import_react_native9.Pressable,
2229
+ import_react_native10.Pressable,
2141
2230
  {
2142
2231
  onPress: () => {
2143
2232
  if (!disabled) animateOut();
2144
2233
  },
2145
2234
  style: styles.buttonInner,
2146
2235
  android_ripple: { color: "rgba(255, 255, 255, 0.3)", borderless: true },
2147
- children: children ?? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native9.View, {})
2236
+ children: children ?? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native10.View, {})
2148
2237
  }
2149
2238
  )
2150
2239
  }
2151
2240
  ) }),
2152
- badgeCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native9.View, { style: [styles.badge, { backgroundColor: theme.colors.danger }], children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native9.Text, { style: [styles.badgeText, { color: theme.colors.onDanger }], children: badgeCount > 99 ? "99+" : badgeCount }) })
2241
+ badgeCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native10.View, { style: [styles.badge, { backgroundColor: theme.colors.danger }], children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native10.Text, { style: [styles.badgeText, { color: theme.colors.onDanger }], children: badgeCount > 99 ? "99+" : badgeCount }) })
2153
2242
  ]
2154
2243
  }
2155
2244
  );
2156
2245
  }
2157
- var styles = import_react_native9.StyleSheet.create({
2246
+ var styles = import_react_native10.StyleSheet.create({
2158
2247
  floatingButton: {
2159
2248
  position: "absolute",
2160
2249
  justifyContent: "center",
@@ -2191,7 +2280,7 @@ var styles = import_react_native9.StyleSheet.create({
2191
2280
 
2192
2281
  // src/components/overlays/EdgeGlowFrame.tsx
2193
2282
  var React11 = __toESM(require("react"));
2194
- var import_react_native10 = require("react-native");
2283
+ var import_react_native11 = require("react-native");
2195
2284
  var import_expo_linear_gradient = require("expo-linear-gradient");
2196
2285
 
2197
2286
  // src/components/utils/color.ts
@@ -2233,9 +2322,9 @@ function EdgeGlowFrame({
2233
2322
  }) {
2234
2323
  const theme = useTheme();
2235
2324
  const alpha = Math.max(0, Math.min(1, intensity));
2236
- const anim = React11.useRef(new import_react_native10.Animated.Value(visible ? 1 : 0)).current;
2325
+ const anim = React11.useRef(new import_react_native11.Animated.Value(visible ? 1 : 0)).current;
2237
2326
  React11.useEffect(() => {
2238
- import_react_native10.Animated.timing(anim, {
2327
+ import_react_native11.Animated.timing(anim, {
2239
2328
  toValue: visible ? 1 : 0,
2240
2329
  duration: 300,
2241
2330
  useNativeDriver: true
@@ -2244,8 +2333,8 @@ function EdgeGlowFrame({
2244
2333
  const c = baseColor(role, theme);
2245
2334
  const strong = withAlpha(c, 0.6 * alpha);
2246
2335
  const soft = withAlpha(c, 0.22 * alpha);
2247
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native10.Animated.View, { pointerEvents: "none", style: [{ position: "absolute", inset: 0, opacity: anim }, style], children: [
2248
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native10.View, { style: { position: "absolute", top: 0, left: 0, right: 0, height: thickness }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2336
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native11.Animated.View, { pointerEvents: "none", style: [{ position: "absolute", inset: 0, opacity: anim }, style], children: [
2337
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native11.View, { style: { position: "absolute", top: 0, left: 0, right: 0, height: thickness }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2249
2338
  import_expo_linear_gradient.LinearGradient,
2250
2339
  {
2251
2340
  colors: [strong, soft, "transparent"],
@@ -2254,7 +2343,7 @@ function EdgeGlowFrame({
2254
2343
  style: { width: "100%", height: "100%" }
2255
2344
  }
2256
2345
  ) }),
2257
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native10.View, { style: { position: "absolute", bottom: 0, left: 0, right: 0, height: thickness }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2346
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native11.View, { style: { position: "absolute", bottom: 0, left: 0, right: 0, height: thickness }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2258
2347
  import_expo_linear_gradient.LinearGradient,
2259
2348
  {
2260
2349
  colors: ["transparent", soft, strong],
@@ -2263,7 +2352,7 @@ function EdgeGlowFrame({
2263
2352
  style: { width: "100%", height: "100%" }
2264
2353
  }
2265
2354
  ) }),
2266
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native10.View, { style: { position: "absolute", top: 0, bottom: 0, left: 0, width: thickness }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2355
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native11.View, { style: { position: "absolute", top: 0, bottom: 0, left: 0, width: thickness }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2267
2356
  import_expo_linear_gradient.LinearGradient,
2268
2357
  {
2269
2358
  colors: [strong, soft, "transparent"],
@@ -2272,7 +2361,7 @@ function EdgeGlowFrame({
2272
2361
  style: { width: "100%", height: "100%" }
2273
2362
  }
2274
2363
  ) }),
2275
- /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native10.View, { style: { position: "absolute", top: 0, bottom: 0, right: 0, width: thickness }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2364
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react_native11.View, { style: { position: "absolute", top: 0, bottom: 0, right: 0, width: thickness }, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2276
2365
  import_expo_linear_gradient.LinearGradient,
2277
2366
  {
2278
2367
  colors: ["transparent", soft, strong],
@@ -2286,12 +2375,12 @@ function EdgeGlowFrame({
2286
2375
 
2287
2376
  // src/components/draw/DrawModeOverlay.tsx
2288
2377
  var React14 = __toESM(require("react"));
2289
- var import_react_native14 = require("react-native");
2378
+ var import_react_native15 = require("react-native");
2290
2379
  var import_react_native_view_shot = require("react-native-view-shot");
2291
2380
 
2292
2381
  // src/components/draw/DrawSurface.tsx
2293
2382
  var React12 = __toESM(require("react"));
2294
- var import_react_native11 = require("react-native");
2383
+ var import_react_native12 = require("react-native");
2295
2384
  var import_react_native_svg = __toESM(require("react-native-svg"));
2296
2385
 
2297
2386
  // src/components/draw/strokes.ts
@@ -2362,7 +2451,7 @@ function DrawSurface({
2362
2451
  triggerRender();
2363
2452
  }, [color, onAddStroke, strokeWidth, triggerRender]);
2364
2453
  const panResponder = React12.useMemo(
2365
- () => import_react_native11.PanResponder.create({
2454
+ () => import_react_native12.PanResponder.create({
2366
2455
  onStartShouldSetPanResponder: () => true,
2367
2456
  onMoveShouldSetPanResponder: () => true,
2368
2457
  onPanResponderGrant: onStart,
@@ -2374,7 +2463,7 @@ function DrawSurface({
2374
2463
  );
2375
2464
  const currentPath = pointsToSmoothPath(currentPointsRef.current);
2376
2465
  void renderTick;
2377
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native11.View, { style: [import_react_native11.StyleSheet.absoluteFill, styles2.container, style], ...panResponder.panHandlers, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_svg.default, { style: import_react_native11.StyleSheet.absoluteFill, width: "100%", height: "100%", children: [
2466
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native12.View, { style: [import_react_native12.StyleSheet.absoluteFill, styles2.container, style], ...panResponder.panHandlers, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(import_react_native_svg.default, { style: import_react_native12.StyleSheet.absoluteFill, width: "100%", height: "100%", children: [
2378
2467
  strokes.map((s, idx) => {
2379
2468
  const d = pointsToSmoothPath(s.points);
2380
2469
  if (!d) return null;
@@ -2404,7 +2493,7 @@ function DrawSurface({
2404
2493
  ) : null
2405
2494
  ] }) });
2406
2495
  }
2407
- var styles2 = import_react_native11.StyleSheet.create({
2496
+ var styles2 = import_react_native12.StyleSheet.create({
2408
2497
  container: {
2409
2498
  zIndex: 5
2410
2499
  }
@@ -2412,7 +2501,7 @@ var styles2 = import_react_native11.StyleSheet.create({
2412
2501
 
2413
2502
  // src/components/draw/DrawToolbar.tsx
2414
2503
  var React13 = __toESM(require("react"));
2415
- var import_react_native13 = require("react-native");
2504
+ var import_react_native14 = require("react-native");
2416
2505
  var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
2417
2506
  var import_lucide_react_native = require("lucide-react-native");
2418
2507
 
@@ -2431,7 +2520,7 @@ async function impact(style) {
2431
2520
  }
2432
2521
 
2433
2522
  // src/components/draw/DrawColorPicker.tsx
2434
- var import_react_native12 = require("react-native");
2523
+ var import_react_native13 = require("react-native");
2435
2524
  var import_jsx_runtime10 = require("react/jsx-runtime");
2436
2525
  function DrawColorPicker({
2437
2526
  colors,
@@ -2466,10 +2555,10 @@ function DrawColorPicker({
2466
2555
  return { ...base, ...selectedStyle, ...whiteStyle };
2467
2556
  };
2468
2557
  if (!expanded) {
2469
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.Pressable, { onPress: onToggle, style: [swatchStyle(selected, true), style] });
2558
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native13.Pressable, { onPress: onToggle, style: [swatchStyle(selected, true), style] });
2470
2559
  }
2471
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: [{ flexDirection: "row", alignItems: "center", gap: 8 }, style], children: colors.map((c, idx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2472
- import_react_native12.Pressable,
2560
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native13.View, { style: [{ flexDirection: "row", alignItems: "center", gap: 8 }, style], children: colors.map((c, idx) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2561
+ import_react_native13.Pressable,
2473
2562
  {
2474
2563
  onPress: () => {
2475
2564
  onSelect(c);
@@ -2500,14 +2589,14 @@ function DrawToolbar({
2500
2589
  style
2501
2590
  }) {
2502
2591
  const insets = (0, import_react_native_safe_area_context2.useSafeAreaInsets)();
2503
- const { width: screenWidth, height: screenHeight } = (0, import_react_native13.useWindowDimensions)();
2592
+ const { width: screenWidth, height: screenHeight } = (0, import_react_native14.useWindowDimensions)();
2504
2593
  const [expanded, setExpanded] = React13.useState(false);
2505
- const pos = React13.useRef(new import_react_native13.Animated.ValueXY({ x: screenWidth / 2 - 110, y: -140 })).current;
2594
+ const pos = React13.useRef(new import_react_native14.Animated.ValueXY({ x: screenWidth / 2 - 110, y: -140 })).current;
2506
2595
  const start = React13.useRef({ x: 0, y: 0 });
2507
2596
  const currentPos = React13.useRef({ x: 0, y: 0 });
2508
2597
  React13.useEffect(() => {
2509
2598
  if (hidden) return;
2510
- import_react_native13.Animated.spring(pos.y, {
2599
+ import_react_native14.Animated.spring(pos.y, {
2511
2600
  toValue: insets.top + 60,
2512
2601
  useNativeDriver: true,
2513
2602
  damping: 12,
@@ -2534,7 +2623,7 @@ function DrawToolbar({
2534
2623
  [insets.top, screenHeight, screenWidth]
2535
2624
  );
2536
2625
  const panResponder = React13.useMemo(
2537
- () => import_react_native13.PanResponder.create({
2626
+ () => import_react_native14.PanResponder.create({
2538
2627
  onStartShouldSetPanResponder: () => false,
2539
2628
  onMoveShouldSetPanResponder: (_e, g) => Math.abs(g.dx) > 5 || Math.abs(g.dy) > 5,
2540
2629
  onPanResponderGrant: () => {
@@ -2546,7 +2635,7 @@ function DrawToolbar({
2546
2635
  },
2547
2636
  onPanResponderRelease: () => {
2548
2637
  const next = clamp2(currentPos.current.x, currentPos.current.y);
2549
- import_react_native13.Animated.spring(pos, { toValue: next, useNativeDriver: true }).start();
2638
+ import_react_native14.Animated.spring(pos, { toValue: next, useNativeDriver: true }).start();
2550
2639
  }
2551
2640
  }),
2552
2641
  [clamp2, pos]
@@ -2563,7 +2652,7 @@ function DrawToolbar({
2563
2652
  const isDisabled = Boolean(disabled) || Boolean(capturingDisabled);
2564
2653
  const [pressed, setPressed] = React13.useState(false);
2565
2654
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2566
- import_react_native13.View,
2655
+ import_react_native14.View,
2567
2656
  {
2568
2657
  style: {
2569
2658
  width: 28,
@@ -2575,7 +2664,7 @@ function DrawToolbar({
2575
2664
  opacity: isDisabled ? 0.5 : pressed ? 0.85 : 1
2576
2665
  },
2577
2666
  children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2578
- import_react_native13.Pressable,
2667
+ import_react_native14.Pressable,
2579
2668
  {
2580
2669
  accessibilityRole: "button",
2581
2670
  accessibilityLabel,
@@ -2592,7 +2681,7 @@ function DrawToolbar({
2592
2681
  );
2593
2682
  }
2594
2683
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2595
- import_react_native13.Animated.View,
2684
+ import_react_native14.Animated.View,
2596
2685
  {
2597
2686
  style: [
2598
2687
  {
@@ -2609,7 +2698,7 @@ function DrawToolbar({
2609
2698
  ],
2610
2699
  ...panResponder.panHandlers,
2611
2700
  children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2612
- import_react_native13.View,
2701
+ import_react_native14.View,
2613
2702
  {
2614
2703
  style: {
2615
2704
  backgroundColor: "#F43F5E",
@@ -2617,7 +2706,7 @@ function DrawToolbar({
2617
2706
  padding: 12,
2618
2707
  minWidth: 220
2619
2708
  },
2620
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native13.View, { style: { flexDirection: "row", alignItems: "center", gap: 8 }, children: [
2709
+ children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native14.View, { style: { flexDirection: "row", alignItems: "center", gap: 8 }, children: [
2621
2710
  renderDragHandle ? renderDragHandle() : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react_native.GripVertical, { size: 20, color: "rgba(255, 255, 255, 0.6)" }),
2622
2711
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2623
2712
  DrawColorPicker,
@@ -2635,7 +2724,7 @@ function DrawToolbar({
2635
2724
  }
2636
2725
  }
2637
2726
  ),
2638
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native13.View, { style: { width: 1, height: 20, backgroundColor: "rgba(255, 255, 255, 0.3)", marginHorizontal: 4 } }),
2727
+ /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native14.View, { style: { width: 1, height: 20, backgroundColor: "rgba(255, 255, 255, 0.3)", marginHorizontal: 4 } }),
2639
2728
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2640
2729
  CircleActionButton,
2641
2730
  {
@@ -2673,7 +2762,7 @@ function DrawToolbar({
2673
2762
  void impact("medium");
2674
2763
  onDone();
2675
2764
  },
2676
- children: capturing ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native13.ActivityIndicator, { color: "#FFFFFF", size: "small" }) : renderDoneIcon ? renderDoneIcon() : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react_native.Check, { size: 16, color: "#FFFFFF" })
2765
+ children: capturing ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native14.ActivityIndicator, { color: "#FFFFFF", size: "small" }) : renderDoneIcon ? renderDoneIcon() : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react_native.Check, { size: 16, color: "#FFFFFF" })
2677
2766
  }
2678
2767
  )
2679
2768
  ] })
@@ -2758,7 +2847,7 @@ function DrawModeOverlay({
2758
2847
  }
2759
2848
  }, [captureTargetRef, capturing, onCapture]);
2760
2849
  if (!visible) return null;
2761
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native14.View, { style: [import_react_native14.StyleSheet.absoluteFill, styles3.root, style], pointerEvents: "box-none", children: [
2850
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(import_react_native15.View, { style: [import_react_native15.StyleSheet.absoluteFill, styles3.root, style], pointerEvents: "box-none", children: [
2762
2851
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(EdgeGlowFrame, { visible: !hideUi, role: "danger", thickness: 50, intensity: 1 }),
2763
2852
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2764
2853
  DrawSurface,
@@ -2789,7 +2878,7 @@ function DrawModeOverlay({
2789
2878
  )
2790
2879
  ] });
2791
2880
  }
2792
- var styles3 = import_react_native14.StyleSheet.create({
2881
+ var styles3 = import_react_native15.StyleSheet.create({
2793
2882
  root: {
2794
2883
  zIndex: 9999
2795
2884
  }
@@ -2797,7 +2886,7 @@ var styles3 = import_react_native14.StyleSheet.create({
2797
2886
 
2798
2887
  // src/components/comments/AppCommentsSheet.tsx
2799
2888
  var React21 = __toESM(require("react"));
2800
- var import_react_native20 = require("react-native");
2889
+ var import_react_native21 = require("react-native");
2801
2890
  var import_bottom_sheet3 = require("@gorhom/bottom-sheet");
2802
2891
  var import_react_native_safe_area_context3 = require("react-native-safe-area-context");
2803
2892
  var import_liquid_glass4 = require("@callstack/liquid-glass");
@@ -2805,13 +2894,13 @@ var import_lucide_react_native4 = require("lucide-react-native");
2805
2894
 
2806
2895
  // src/components/chat/ChatComposer.tsx
2807
2896
  var React16 = __toESM(require("react"));
2808
- var import_react_native16 = require("react-native");
2897
+ var import_react_native17 = require("react-native");
2809
2898
  var import_liquid_glass3 = require("@callstack/liquid-glass");
2810
2899
  var import_lucide_react_native3 = require("lucide-react-native");
2811
2900
 
2812
2901
  // src/components/chat/MultilineTextInput.tsx
2813
2902
  var React15 = __toESM(require("react"));
2814
- var import_react_native15 = require("react-native");
2903
+ var import_react_native16 = require("react-native");
2815
2904
  var import_bottom_sheet2 = require("@gorhom/bottom-sheet");
2816
2905
  var import_jsx_runtime13 = require("react/jsx-runtime");
2817
2906
  var MultilineTextInput = React15.forwardRef(function MultilineTextInput2({ useBottomSheetTextInput = false, placeholder, placeholderTextColor, style, ...props }, ref) {
@@ -2834,7 +2923,7 @@ var MultilineTextInput = React15.forwardRef(function MultilineTextInput2({ useBo
2834
2923
  style: [baseStyle, style],
2835
2924
  textAlignVertical: "top"
2836
2925
  };
2837
- return useBottomSheetTextInput ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_bottom_sheet2.BottomSheetTextInput, { ref, ...commonProps }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native15.TextInput, { ref, ...commonProps });
2926
+ return useBottomSheetTextInput ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_bottom_sheet2.BottomSheetTextInput, { ref, ...commonProps }) : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native16.TextInput, { ref, ...commonProps });
2838
2927
  });
2839
2928
 
2840
2929
  // src/components/icons/StudioIcons.tsx
@@ -2887,9 +2976,9 @@ function AspectRatioThumbnail({
2887
2976
  renderRemoveIcon
2888
2977
  }) {
2889
2978
  const [aspectRatio, setAspectRatio] = React16.useState(1);
2890
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native16.View, { style: { height: THUMBNAIL_HEIGHT, aspectRatio, position: "relative" }, children: [
2891
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native16.View, { style: { flex: 1, borderRadius: 8, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2892
- import_react_native16.Image,
2979
+ return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native17.View, { style: { height: THUMBNAIL_HEIGHT, aspectRatio, position: "relative" }, children: [
2980
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native17.View, { style: { flex: 1, borderRadius: 8, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2981
+ import_react_native17.Image,
2893
2982
  {
2894
2983
  source: { uri },
2895
2984
  style: { width: "100%", height: "100%" },
@@ -2902,7 +2991,7 @@ function AspectRatioThumbnail({
2902
2991
  }
2903
2992
  ) }),
2904
2993
  onRemove ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2905
- import_react_native16.Pressable,
2994
+ import_react_native17.Pressable,
2906
2995
  {
2907
2996
  style: {
2908
2997
  position: "absolute",
@@ -2950,8 +3039,8 @@ function ChatComposer({
2950
3039
  const hasText = text.trim().length > 0;
2951
3040
  const composerMinHeight = hasAttachments ? THUMBNAIL_HEIGHT + 44 + 24 : 44;
2952
3041
  const isButtonDisabled = sending || disabled || sendDisabled;
2953
- const maxInputHeight = React16.useMemo(() => import_react_native16.Dimensions.get("window").height * 0.5, []);
2954
- const shakeAnim = React16.useRef(new import_react_native16.Animated.Value(0)).current;
3042
+ const maxInputHeight = React16.useMemo(() => import_react_native17.Dimensions.get("window").height * 0.5, []);
3043
+ const shakeAnim = React16.useRef(new import_react_native17.Animated.Value(0)).current;
2955
3044
  const [sendPressed, setSendPressed] = React16.useState(false);
2956
3045
  const inputRef = React16.useRef(null);
2957
3046
  const prevAutoFocusRef = React16.useRef(false);
@@ -2967,12 +3056,12 @@ function ChatComposer({
2967
3056
  }, [autoFocus, disabled, sending]);
2968
3057
  const triggerShake = React16.useCallback(() => {
2969
3058
  shakeAnim.setValue(0);
2970
- import_react_native16.Animated.sequence([
2971
- import_react_native16.Animated.timing(shakeAnim, { toValue: 10, duration: 50, useNativeDriver: true }),
2972
- import_react_native16.Animated.timing(shakeAnim, { toValue: -10, duration: 50, useNativeDriver: true }),
2973
- import_react_native16.Animated.timing(shakeAnim, { toValue: 10, duration: 50, useNativeDriver: true }),
2974
- import_react_native16.Animated.timing(shakeAnim, { toValue: -10, duration: 50, useNativeDriver: true }),
2975
- import_react_native16.Animated.timing(shakeAnim, { toValue: 0, duration: 50, useNativeDriver: true })
3059
+ import_react_native17.Animated.sequence([
3060
+ import_react_native17.Animated.timing(shakeAnim, { toValue: 10, duration: 50, useNativeDriver: true }),
3061
+ import_react_native17.Animated.timing(shakeAnim, { toValue: -10, duration: 50, useNativeDriver: true }),
3062
+ import_react_native17.Animated.timing(shakeAnim, { toValue: 10, duration: 50, useNativeDriver: true }),
3063
+ import_react_native17.Animated.timing(shakeAnim, { toValue: -10, duration: 50, useNativeDriver: true }),
3064
+ import_react_native17.Animated.timing(shakeAnim, { toValue: 0, duration: 50, useNativeDriver: true })
2976
3065
  ]).start();
2977
3066
  }, [shakeAnim]);
2978
3067
  const handleSend = React16.useCallback(async () => {
@@ -2988,12 +3077,12 @@ function ChatComposer({
2988
3077
  const textareaBgColor = theme.scheme === "dark" ? "#18181B" : "#F6F6F6";
2989
3078
  const placeholderTextColor = theme.scheme === "dark" ? "#A1A1AA" : "#71717A";
2990
3079
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2991
- import_react_native16.View,
3080
+ import_react_native17.View,
2992
3081
  {
2993
3082
  style: [{ paddingHorizontal: 16, paddingBottom: 12, paddingTop: 8 }, style],
2994
3083
  onLayout: (e) => onLayout == null ? void 0 : onLayout({ height: e.nativeEvent.layout.height }),
2995
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native16.View, { style: { flexDirection: "row", alignItems: "flex-end", gap: 8 }, children: [
2996
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native16.Animated.View, { style: { flex: 1, transform: [{ translateX: shakeAnim }] }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3084
+ children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native17.View, { style: { flexDirection: "row", alignItems: "flex-end", gap: 8 }, children: [
3085
+ /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native17.Animated.View, { style: { flex: 1, transform: [{ translateX: shakeAnim }] }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2997
3086
  import_liquid_glass3.LiquidGlassView,
2998
3087
  {
2999
3088
  style: [
@@ -3006,7 +3095,7 @@ function ChatComposer({
3006
3095
  effect: "clear",
3007
3096
  children: [
3008
3097
  hasAttachments ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
3009
- import_react_native16.ScrollView,
3098
+ import_react_native17.ScrollView,
3010
3099
  {
3011
3100
  horizontal: true,
3012
3101
  showsHorizontalScrollIndicator: false,
@@ -3023,7 +3112,7 @@ function ChatComposer({
3023
3112
  `attachment-${index}`
3024
3113
  )),
3025
3114
  onAddAttachment ? renderAddAttachment ? renderAddAttachment() : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3026
- import_react_native16.Pressable,
3115
+ import_react_native17.Pressable,
3027
3116
  {
3028
3117
  style: {
3029
3118
  height: THUMBNAIL_HEIGHT,
@@ -3075,7 +3164,7 @@ function ChatComposer({
3075
3164
  interactive: true,
3076
3165
  effect: "clear",
3077
3166
  children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3078
- import_react_native16.View,
3167
+ import_react_native17.View,
3079
3168
  {
3080
3169
  style: {
3081
3170
  width: 44,
@@ -3086,7 +3175,7 @@ function ChatComposer({
3086
3175
  opacity: isButtonDisabled ? 0.6 : sendPressed ? 0.9 : 1
3087
3176
  },
3088
3177
  children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3089
- import_react_native16.Pressable,
3178
+ import_react_native17.Pressable,
3090
3179
  {
3091
3180
  accessibilityRole: "button",
3092
3181
  accessibilityLabel: "Send",
@@ -3095,7 +3184,7 @@ function ChatComposer({
3095
3184
  onPressIn: () => setSendPressed(true),
3096
3185
  onPressOut: () => setSendPressed(false),
3097
3186
  style: { flex: 1, alignItems: "center", justifyContent: "center" },
3098
- children: sending ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native16.ActivityIndicator, {}) : renderSendIcon ? renderSendIcon() : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IconChevronRight, { size: 20, colorToken: "onPrimary" })
3187
+ children: sending ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native17.ActivityIndicator, {}) : renderSendIcon ? renderSendIcon() : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IconChevronRight, { size: 20, colorToken: "onPrimary" })
3099
3188
  }
3100
3189
  )
3101
3190
  }
@@ -3109,10 +3198,10 @@ function ChatComposer({
3109
3198
 
3110
3199
  // src/components/comments/CommentRow.tsx
3111
3200
  var React17 = __toESM(require("react"));
3112
- var import_react_native18 = require("react-native");
3201
+ var import_react_native19 = require("react-native");
3113
3202
 
3114
3203
  // src/components/primitives/Avatar.tsx
3115
- var import_react_native17 = require("react-native");
3204
+ var import_react_native18 = require("react-native");
3116
3205
  var import_jsx_runtime16 = require("react/jsx-runtime");
3117
3206
  function initialsFrom(name) {
3118
3207
  var _a, _b;
@@ -3132,7 +3221,7 @@ function Avatar({
3132
3221
  const radius = size / 2;
3133
3222
  const fallbackBg = fallbackBackgroundColor ?? theme.colors.neutral;
3134
3223
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3135
- import_react_native17.View,
3224
+ import_react_native18.View,
3136
3225
  {
3137
3226
  style: [
3138
3227
  {
@@ -3147,7 +3236,7 @@ function Avatar({
3147
3236
  style
3148
3237
  ],
3149
3238
  children: uri ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3150
- import_react_native17.Image,
3239
+ import_react_native18.Image,
3151
3240
  {
3152
3241
  source: { uri },
3153
3242
  style: [{ width: size, height: size }, imageStyle],
@@ -3198,7 +3287,7 @@ function CommentRow({ comment, showDivider }) {
3198
3287
  };
3199
3288
  }, [comment.authorId]);
3200
3289
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3201
- import_react_native18.View,
3290
+ import_react_native19.View,
3202
3291
  {
3203
3292
  style: {
3204
3293
  flexDirection: "row",
@@ -3209,8 +3298,8 @@ function CommentRow({ comment, showDivider }) {
3209
3298
  },
3210
3299
  children: [
3211
3300
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Avatar, { size: 32, uri: authorAvatar, name: authorName ?? comment.authorId, style: { marginTop: 6 } }),
3212
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react_native18.View, { style: { flex: 1, minWidth: 0, gap: 4 }, children: [
3213
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react_native18.View, { style: { flexDirection: "row", alignItems: "center", gap: theme.spacing.sm }, children: [
3301
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react_native19.View, { style: { flex: 1, minWidth: 0, gap: 4 }, children: [
3302
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react_native19.View, { style: { flexDirection: "row", alignItems: "center", gap: theme.spacing.sm }, children: [
3214
3303
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { style: { fontSize: 14, lineHeight: 18, fontWeight: theme.typography.fontWeight.bold, color: theme.colors.text }, children: authorName ?? "Unknown User" }),
3215
3304
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { style: { fontSize: 12, lineHeight: 16, color: theme.colors.textMuted }, children: formatTimeAgo(comment.createdAt) })
3216
3305
  ] }),
@@ -3378,13 +3467,13 @@ function useAppDetails(appId) {
3378
3467
 
3379
3468
  // src/components/comments/useIosKeyboardSnapFix.ts
3380
3469
  var React20 = __toESM(require("react"));
3381
- var import_react_native19 = require("react-native");
3470
+ var import_react_native20 = require("react-native");
3382
3471
  function useIosKeyboardSnapFix(sheetRef, options) {
3383
3472
  const [keyboardVisible, setKeyboardVisible] = React20.useState(false);
3384
3473
  React20.useEffect(() => {
3385
- if (import_react_native19.Platform.OS !== "ios") return;
3386
- const show = import_react_native19.Keyboard.addListener("keyboardWillShow", () => setKeyboardVisible(true));
3387
- const hide = import_react_native19.Keyboard.addListener("keyboardWillHide", () => {
3474
+ if (import_react_native20.Platform.OS !== "ios") return;
3475
+ const show = import_react_native20.Keyboard.addListener("keyboardWillShow", () => setKeyboardVisible(true));
3476
+ const hide = import_react_native20.Keyboard.addListener("keyboardWillHide", () => {
3388
3477
  var _a;
3389
3478
  setKeyboardVisible(false);
3390
3479
  const target = (options == null ? void 0 : options.targetIndex) ?? 1;
@@ -3460,17 +3549,17 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3460
3549
  onChange: handleChange,
3461
3550
  backgroundStyle: {
3462
3551
  backgroundColor: theme.scheme === "dark" ? "#0B080F" : "#FFFFFF",
3463
- borderTopLeftRadius: import_react_native20.Platform.OS === "ios" ? 39 : 16,
3464
- borderTopRightRadius: import_react_native20.Platform.OS === "ios" ? 39 : 16
3552
+ borderTopLeftRadius: import_react_native21.Platform.OS === "ios" ? 39 : 16,
3553
+ borderTopRightRadius: import_react_native21.Platform.OS === "ios" ? 39 : 16
3465
3554
  },
3466
3555
  handleIndicatorStyle: { backgroundColor: theme.colors.handleIndicator },
3467
3556
  keyboardBehavior: "interactive",
3468
3557
  keyboardBlurBehavior: "restore",
3469
3558
  android_keyboardInputMode: "adjustResize",
3470
3559
  topInset: insets.top,
3471
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native20.View, { style: { flex: 1 }, children: [
3560
+ children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native21.View, { style: { flex: 1 }, children: [
3472
3561
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
3473
- import_react_native20.View,
3562
+ import_react_native21.View,
3474
3563
  {
3475
3564
  style: {
3476
3565
  flexDirection: "row",
@@ -3506,7 +3595,7 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3506
3595
  interactive: true,
3507
3596
  effect: "clear",
3508
3597
  children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3509
- import_react_native20.View,
3598
+ import_react_native21.View,
3510
3599
  {
3511
3600
  style: {
3512
3601
  width: 32,
@@ -3518,7 +3607,7 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3518
3607
  opacity: appId ? 1 : 0.5
3519
3608
  },
3520
3609
  children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3521
- import_react_native20.Pressable,
3610
+ import_react_native21.Pressable,
3522
3611
  {
3523
3612
  disabled: !appId,
3524
3613
  onPress: () => void handlePlay(),
@@ -3553,13 +3642,13 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3553
3642
  },
3554
3643
  keyboardShouldPersistTaps: "handled",
3555
3644
  children: [
3556
- loading && comments.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native20.View, { style: { flex: 1, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native20.ActivityIndicator, {}) }) : comments.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native20.View, { style: { flex: 1, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { variant: "bodyMuted", style: { textAlign: "center" }, children: "No comments yet" }) }) : comments.map((c, idx) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(CommentRow, { comment: c, showDivider: idx < comments.length - 1 }, c.id)),
3645
+ loading && comments.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.View, { style: { flex: 1, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.ActivityIndicator, {}) }) : comments.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_react_native21.View, { style: { flex: 1, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { variant: "bodyMuted", style: { textAlign: "center" }, children: "No comments yet" }) }) : comments.map((c, idx) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(CommentRow, { comment: c, showDivider: idx < comments.length - 1 }, c.id)),
3557
3646
  error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { variant: "captionMuted", style: { marginTop: theme.spacing.lg }, children: "Failed to load comments." }) : null
3558
3647
  ]
3559
3648
  }
3560
3649
  ),
3561
3650
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3562
- import_react_native20.View,
3651
+ import_react_native21.View,
3563
3652
  {
3564
3653
  style: {
3565
3654
  position: "absolute",
@@ -3568,7 +3657,7 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3568
3657
  bottom: 0,
3569
3658
  paddingHorizontal: theme.spacing.lg,
3570
3659
  paddingTop: theme.spacing.sm,
3571
- paddingBottom: import_react_native20.Platform.OS === "ios" ? keyboardVisible ? theme.spacing.lg : insets.bottom : insets.bottom + 10,
3660
+ paddingBottom: import_react_native21.Platform.OS === "ios" ? keyboardVisible ? theme.spacing.lg : insets.bottom : insets.bottom + 10,
3572
3661
  borderTopWidth: 1,
3573
3662
  borderTopColor: withAlpha(theme.colors.border, 0.1),
3574
3663
  backgroundColor: withAlpha(theme.colors.background, 0.8)
@@ -3582,7 +3671,7 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3582
3671
  useBottomSheetTextInput: true,
3583
3672
  onSend: async (text) => {
3584
3673
  await create(text);
3585
- import_react_native20.Keyboard.dismiss();
3674
+ import_react_native21.Keyboard.dismiss();
3586
3675
  }
3587
3676
  }
3588
3677
  )
@@ -3594,16 +3683,16 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3594
3683
  }
3595
3684
 
3596
3685
  // src/studio/ui/PreviewPanel.tsx
3597
- var import_react_native41 = require("react-native");
3686
+ var import_react_native42 = require("react-native");
3598
3687
 
3599
3688
  // src/components/preview/PreviewPage.tsx
3600
- var import_react_native21 = require("react-native");
3689
+ var import_react_native22 = require("react-native");
3601
3690
  var import_bottom_sheet4 = require("@gorhom/bottom-sheet");
3602
3691
  var import_jsx_runtime19 = require("react/jsx-runtime");
3603
3692
  function PreviewPage({ header, children, contentStyle }) {
3604
3693
  const theme = useTheme();
3605
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native21.View, { style: { flex: 1 }, children: [
3606
- header ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native21.View, { children: header }) : null,
3694
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native22.View, { style: { flex: 1 }, children: [
3695
+ header ? /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(import_react_native22.View, { children: header }) : null,
3607
3696
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3608
3697
  import_bottom_sheet4.BottomSheetScrollView,
3609
3698
  {
@@ -3623,15 +3712,15 @@ function PreviewPage({ header, children, contentStyle }) {
3623
3712
  }
3624
3713
 
3625
3714
  // src/studio/ui/preview-panel/PreviewPanelHeader.tsx
3626
- var import_react_native24 = require("react-native");
3715
+ var import_react_native25 = require("react-native");
3627
3716
 
3628
3717
  // src/components/studio-sheet/StudioSheetHeader.tsx
3629
- var import_react_native22 = require("react-native");
3718
+ var import_react_native23 = require("react-native");
3630
3719
  var import_jsx_runtime20 = require("react/jsx-runtime");
3631
3720
  function StudioSheetHeader({ left, center, right, style }) {
3632
3721
  const theme = useTheme();
3633
3722
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
3634
- import_react_native22.View,
3723
+ import_react_native23.View,
3635
3724
  {
3636
3725
  style: [
3637
3726
  {
@@ -3644,9 +3733,9 @@ function StudioSheetHeader({ left, center, right, style }) {
3644
3733
  style
3645
3734
  ],
3646
3735
  children: [
3647
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native22.View, { style: { flexDirection: "row", alignItems: "center" }, children: left }),
3648
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native22.View, { style: { flex: 1, alignItems: "center" }, children: center }),
3649
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native22.View, { style: { flexDirection: "row", alignItems: "center" }, children: right })
3736
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native23.View, { style: { flexDirection: "row", alignItems: "center" }, children: left }),
3737
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native23.View, { style: { flex: 1, alignItems: "center" }, children: center }),
3738
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native23.View, { style: { flexDirection: "row", alignItems: "center" }, children: right })
3650
3739
  ]
3651
3740
  }
3652
3741
  );
@@ -3654,7 +3743,7 @@ function StudioSheetHeader({ left, center, right, style }) {
3654
3743
 
3655
3744
  // src/components/studio-sheet/StudioSheetHeaderIconButton.tsx
3656
3745
  var React22 = __toESM(require("react"));
3657
- var import_react_native23 = require("react-native");
3746
+ var import_react_native24 = require("react-native");
3658
3747
  var import_liquid_glass5 = require("@callstack/liquid-glass");
3659
3748
  var import_jsx_runtime21 = require("react/jsx-runtime");
3660
3749
  function StudioSheetHeaderIconButton({
@@ -3673,14 +3762,14 @@ function StudioSheetHeaderIconButton({
3673
3762
  const glassFallbackBg = theme.scheme === "dark" ? "#18181B" : "#F6F6F6";
3674
3763
  const glassInnerBg = intent === "danger" ? theme.colors.danger : theme.colors.primary;
3675
3764
  const resolvedOpacity = disabled ? 0.6 : pressed ? 0.9 : 1;
3676
- return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native23.View, { style, children: appearance === "glass" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3765
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native24.View, { style, children: appearance === "glass" ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3677
3766
  import_liquid_glass5.LiquidGlassView,
3678
3767
  {
3679
3768
  style: [{ borderRadius: 100 }, !import_liquid_glass5.isLiquidGlassSupported && { backgroundColor: glassFallbackBg }],
3680
3769
  interactive: true,
3681
3770
  effect: "clear",
3682
3771
  children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3683
- import_react_native23.View,
3772
+ import_react_native24.View,
3684
3773
  {
3685
3774
  style: {
3686
3775
  width: size,
@@ -3692,7 +3781,7 @@ function StudioSheetHeaderIconButton({
3692
3781
  opacity: resolvedOpacity
3693
3782
  },
3694
3783
  children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3695
- import_react_native23.Pressable,
3784
+ import_react_native24.Pressable,
3696
3785
  {
3697
3786
  accessibilityRole: "button",
3698
3787
  accessibilityLabel,
@@ -3711,7 +3800,7 @@ function StudioSheetHeaderIconButton({
3711
3800
  )
3712
3801
  }
3713
3802
  ) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3714
- import_react_native23.View,
3803
+ import_react_native24.View,
3715
3804
  {
3716
3805
  style: {
3717
3806
  width: size,
@@ -3723,7 +3812,7 @@ function StudioSheetHeaderIconButton({
3723
3812
  opacity: resolvedOpacity
3724
3813
  },
3725
3814
  children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3726
- import_react_native23.Pressable,
3815
+ import_react_native24.Pressable,
3727
3816
  {
3728
3817
  accessibilityRole: "button",
3729
3818
  accessibilityLabel,
@@ -3750,7 +3839,7 @@ function PreviewPanelHeader({ isOwner, onClose, onNavigateHome, onGoToChat }) {
3750
3839
  {
3751
3840
  left: onNavigateHome ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StudioSheetHeaderIconButton, { onPress: onNavigateHome, accessibilityLabel: "Home", appearance: "glass", intent: "primary", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(IconHome, { size: 20, colorToken: "onPrimary" }) }) : null,
3752
3841
  center: null,
3753
- right: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react_native24.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
3842
+ right: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react_native25.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
3754
3843
  isOwner ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3755
3844
  StudioSheetHeaderIconButton,
3756
3845
  {
@@ -3769,10 +3858,10 @@ function PreviewPanelHeader({ isOwner, onClose, onNavigateHome, onGoToChat }) {
3769
3858
  }
3770
3859
 
3771
3860
  // src/components/preview/PreviewHeroCard.tsx
3772
- var import_react_native26 = require("react-native");
3861
+ var import_react_native27 = require("react-native");
3773
3862
 
3774
3863
  // src/components/primitives/Surface.tsx
3775
- var import_react_native25 = require("react-native");
3864
+ var import_react_native26 = require("react-native");
3776
3865
  var import_jsx_runtime23 = require("react/jsx-runtime");
3777
3866
  function backgroundFor(variant, theme) {
3778
3867
  const { colors } = theme;
@@ -3791,7 +3880,7 @@ function backgroundFor(variant, theme) {
3791
3880
  function Surface({ variant = "surface", border = false, style, ...props }) {
3792
3881
  const theme = useTheme();
3793
3882
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3794
- import_react_native25.View,
3883
+ import_react_native26.View,
3795
3884
  {
3796
3885
  ...props,
3797
3886
  style: [
@@ -3847,11 +3936,11 @@ function PreviewHeroCard({
3847
3936
  },
3848
3937
  style
3849
3938
  ],
3850
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_native26.View, { style: { flex: 1 }, children: [
3851
- background ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native26.View, { style: { position: "absolute", inset: 0 }, children: background }) : null,
3852
- image ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native26.View, { style: { position: "absolute", inset: 0 }, children: image }) : null,
3853
- overlayTopLeft ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native26.View, { style: { position: "absolute", top: theme.spacing.sm, left: theme.spacing.sm, zIndex: 2 }, children: overlayTopLeft }) : null,
3854
- overlayBottom ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native26.View, { style: { flex: 1, justifyContent: "flex-end" }, children: overlayBottom }) : null
3939
+ children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_native27.View, { style: { flex: 1 }, children: [
3940
+ background ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native27.View, { style: { position: "absolute", inset: 0 }, children: background }) : null,
3941
+ image ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native27.View, { style: { position: "absolute", inset: 0 }, children: image }) : null,
3942
+ overlayTopLeft ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native27.View, { style: { position: "absolute", top: theme.spacing.sm, left: theme.spacing.sm, zIndex: 2 }, children: overlayTopLeft }) : null,
3943
+ overlayBottom ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native27.View, { style: { flex: 1, justifyContent: "flex-end" }, children: overlayBottom }) : null
3855
3944
  ] })
3856
3945
  }
3857
3946
  );
@@ -3859,20 +3948,20 @@ function PreviewHeroCard({
3859
3948
 
3860
3949
  // src/components/preview/PreviewPlaceholder.tsx
3861
3950
  var React23 = __toESM(require("react"));
3862
- var import_react_native27 = require("react-native");
3951
+ var import_react_native28 = require("react-native");
3863
3952
  var import_expo_linear_gradient2 = require("expo-linear-gradient");
3864
3953
  var import_jsx_runtime26 = require("react/jsx-runtime");
3865
3954
  function PreviewPlaceholder({ visible, style }) {
3866
3955
  if (!visible) return null;
3867
- const opacityAnim = React23.useRef(new import_react_native27.Animated.Value(0)).current;
3956
+ const opacityAnim = React23.useRef(new import_react_native28.Animated.Value(0)).current;
3868
3957
  React23.useEffect(() => {
3869
3958
  if (!visible) return;
3870
- const animation = import_react_native27.Animated.loop(
3871
- import_react_native27.Animated.sequence([
3872
- import_react_native27.Animated.timing(opacityAnim, { toValue: 1, duration: 1500, useNativeDriver: true }),
3873
- import_react_native27.Animated.timing(opacityAnim, { toValue: 2, duration: 1500, useNativeDriver: true }),
3874
- import_react_native27.Animated.timing(opacityAnim, { toValue: 3, duration: 1500, useNativeDriver: true }),
3875
- import_react_native27.Animated.timing(opacityAnim, { toValue: 0, duration: 1500, useNativeDriver: true })
3959
+ const animation = import_react_native28.Animated.loop(
3960
+ import_react_native28.Animated.sequence([
3961
+ import_react_native28.Animated.timing(opacityAnim, { toValue: 1, duration: 1500, useNativeDriver: true }),
3962
+ import_react_native28.Animated.timing(opacityAnim, { toValue: 2, duration: 1500, useNativeDriver: true }),
3963
+ import_react_native28.Animated.timing(opacityAnim, { toValue: 3, duration: 1500, useNativeDriver: true }),
3964
+ import_react_native28.Animated.timing(opacityAnim, { toValue: 0, duration: 1500, useNativeDriver: true })
3876
3965
  ])
3877
3966
  );
3878
3967
  animation.start();
@@ -3883,7 +3972,7 @@ function PreviewPlaceholder({ visible, style }) {
3883
3972
  const opacity3 = opacityAnim.interpolate({ inputRange: [0, 1, 2, 3], outputRange: [0, 0, 1, 0] });
3884
3973
  const opacity4 = opacityAnim.interpolate({ inputRange: [0, 1, 2, 3], outputRange: [0, 0, 0, 1] });
3885
3974
  return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
3886
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native27.Animated.View, { style: [{ position: "absolute", inset: 0, opacity: opacity1 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3975
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native28.Animated.View, { style: [{ position: "absolute", inset: 0, opacity: opacity1 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3887
3976
  import_expo_linear_gradient2.LinearGradient,
3888
3977
  {
3889
3978
  colors: ["rgba(98, 0, 238, 0.45)", "rgba(168, 85, 247, 0.35)"],
@@ -3892,7 +3981,7 @@ function PreviewPlaceholder({ visible, style }) {
3892
3981
  style: { width: "100%", height: "100%" }
3893
3982
  }
3894
3983
  ) }),
3895
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native27.Animated.View, { style: [{ position: "absolute", inset: 0, opacity: opacity2 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3984
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native28.Animated.View, { style: [{ position: "absolute", inset: 0, opacity: opacity2 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3896
3985
  import_expo_linear_gradient2.LinearGradient,
3897
3986
  {
3898
3987
  colors: ["rgba(168, 85, 247, 0.45)", "rgba(139, 92, 246, 0.35)"],
@@ -3901,7 +3990,7 @@ function PreviewPlaceholder({ visible, style }) {
3901
3990
  style: { width: "100%", height: "100%" }
3902
3991
  }
3903
3992
  ) }),
3904
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native27.Animated.View, { style: [{ position: "absolute", inset: 0, opacity: opacity3 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3993
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native28.Animated.View, { style: [{ position: "absolute", inset: 0, opacity: opacity3 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3905
3994
  import_expo_linear_gradient2.LinearGradient,
3906
3995
  {
3907
3996
  colors: ["rgba(139, 92, 246, 0.45)", "rgba(126, 34, 206, 0.35)"],
@@ -3910,7 +3999,7 @@ function PreviewPlaceholder({ visible, style }) {
3910
3999
  style: { width: "100%", height: "100%" }
3911
4000
  }
3912
4001
  ) }),
3913
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native27.Animated.View, { style: [{ position: "absolute", inset: 0, opacity: opacity4 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
4002
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(import_react_native28.Animated.View, { style: [{ position: "absolute", inset: 0, opacity: opacity4 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3914
4003
  import_expo_linear_gradient2.LinearGradient,
3915
4004
  {
3916
4005
  colors: ["rgba(126, 34, 206, 0.45)", "rgba(98, 0, 238, 0.35)"],
@@ -3923,12 +4012,12 @@ function PreviewPlaceholder({ visible, style }) {
3923
4012
  }
3924
4013
 
3925
4014
  // src/components/preview/PreviewImage.tsx
3926
- var import_react_native28 = require("react-native");
4015
+ var import_react_native29 = require("react-native");
3927
4016
  var import_jsx_runtime27 = require("react/jsx-runtime");
3928
4017
  function PreviewImage({ uri, onLoad, style }) {
3929
4018
  if (!uri) return null;
3930
4019
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3931
- import_react_native28.Image,
4020
+ import_react_native29.Image,
3932
4021
  {
3933
4022
  source: { uri },
3934
4023
  resizeMode: "cover",
@@ -3939,7 +4028,7 @@ function PreviewImage({ uri, onLoad, style }) {
3939
4028
  }
3940
4029
 
3941
4030
  // src/components/preview/StatsBar.tsx
3942
- var import_react_native29 = require("react-native");
4031
+ var import_react_native30 = require("react-native");
3943
4032
  var import_liquid_glass6 = require("@callstack/liquid-glass");
3944
4033
  var import_lucide_react_native5 = require("lucide-react-native");
3945
4034
 
@@ -3972,7 +4061,7 @@ function StatsBar({
3972
4061
  const theme = useTheme();
3973
4062
  const statsBgColor = theme.scheme === "dark" ? "rgba(24, 24, 27, 0.5)" : "rgba(255, 255, 255, 0.5)";
3974
4063
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3975
- import_react_native29.View,
4064
+ import_react_native30.View,
3976
4065
  {
3977
4066
  style: [
3978
4067
  { position: "absolute", bottom: 12, width: "100%", paddingHorizontal: 12 },
@@ -3988,15 +4077,15 @@ function StatsBar({
3988
4077
  !import_liquid_glass6.isLiquidGlassSupported && { backgroundColor: statsBgColor }
3989
4078
  ],
3990
4079
  effect: "clear",
3991
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_react_native29.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingHorizontal: 16 }, children: [
4080
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_react_native30.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingHorizontal: 16 }, children: [
3992
4081
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3993
- import_react_native29.Pressable,
4082
+ import_react_native30.Pressable,
3994
4083
  {
3995
4084
  disabled: !onPressLike,
3996
4085
  onPress: onPressLike,
3997
4086
  hitSlop: 8,
3998
4087
  style: { paddingVertical: 8 },
3999
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_react_native29.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
4088
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_react_native30.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
4000
4089
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
4001
4090
  import_lucide_react_native5.Heart,
4002
4091
  {
@@ -4006,7 +4095,7 @@ function StatsBar({
4006
4095
  fill: isLiked ? theme.colors.danger : "transparent"
4007
4096
  }
4008
4097
  ),
4009
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_native29.View, { style: { width: 4 } }),
4098
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_native30.View, { style: { width: 4 } }),
4010
4099
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
4011
4100
  Text,
4012
4101
  {
@@ -4022,22 +4111,22 @@ function StatsBar({
4022
4111
  }
4023
4112
  ),
4024
4113
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
4025
- import_react_native29.Pressable,
4114
+ import_react_native30.Pressable,
4026
4115
  {
4027
4116
  disabled: !onPressComments,
4028
4117
  onPress: onPressComments,
4029
4118
  hitSlop: 8,
4030
4119
  style: { paddingVertical: 8 },
4031
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_react_native29.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
4120
+ children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_react_native30.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
4032
4121
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react_native5.MessageCircle, { size: 16, strokeWidth: 2.5, color: "#FFFFFF" }),
4033
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_native29.View, { style: { width: 4 } }),
4122
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_native30.View, { style: { width: 4 } }),
4034
4123
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Text, { variant: "caption", style: { color: "#FFFFFF", fontWeight: theme.typography.fontWeight.bold }, children: commentCount })
4035
4124
  ] })
4036
4125
  }
4037
4126
  ),
4038
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_react_native29.View, { style: { flexDirection: "row", alignItems: "center", paddingVertical: 8 }, children: [
4039
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_native29.View, { style: { transform: [{ scaleY: -1 }] }, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(MergeIcon, { width: 14, height: 14, color: "#FFFFFF" }) }),
4040
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_native29.View, { style: { width: 4 } }),
4127
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_react_native30.View, { style: { flexDirection: "row", alignItems: "center", paddingVertical: 8 }, children: [
4128
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_native30.View, { style: { transform: [{ scaleY: -1 }] }, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(MergeIcon, { width: 14, height: 14, color: "#FFFFFF" }) }),
4129
+ /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_native30.View, { style: { width: 4 } }),
4041
4130
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Text, { variant: "caption", style: { color: "#FFFFFF", fontWeight: theme.typography.fontWeight.bold }, children: forkCount })
4042
4131
  ] })
4043
4132
  ] })
@@ -4048,7 +4137,7 @@ function StatsBar({
4048
4137
  }
4049
4138
 
4050
4139
  // src/components/preview/PreviewStatusBadge.tsx
4051
- var import_react_native30 = require("react-native");
4140
+ var import_react_native31 = require("react-native");
4052
4141
  var import_lucide_react_native6 = require("lucide-react-native");
4053
4142
 
4054
4143
  // src/data/apps/types.ts
@@ -4093,7 +4182,7 @@ function PreviewStatusBadge({ status }) {
4093
4182
  const IconComp = STATUS_ICON[status];
4094
4183
  const label = APP_STATUS_LABEL[status] ?? status;
4095
4184
  return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
4096
- import_react_native30.View,
4185
+ import_react_native31.View,
4097
4186
  {
4098
4187
  style: {
4099
4188
  flexDirection: "row",
@@ -4146,10 +4235,10 @@ function PreviewHeroSection({
4146
4235
  }
4147
4236
 
4148
4237
  // src/studio/ui/preview-panel/PreviewMetaSection.tsx
4149
- var import_react_native32 = require("react-native");
4238
+ var import_react_native33 = require("react-native");
4150
4239
 
4151
4240
  // src/components/preview/PreviewMetaRow.tsx
4152
- var import_react_native31 = require("react-native");
4241
+ var import_react_native32 = require("react-native");
4153
4242
  var import_jsx_runtime32 = require("react/jsx-runtime");
4154
4243
  function PreviewMetaRow({
4155
4244
  avatarUri,
@@ -4161,10 +4250,10 @@ function PreviewMetaRow({
4161
4250
  style
4162
4251
  }) {
4163
4252
  const theme = useTheme();
4164
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_react_native31.View, { style: [{ alignSelf: "stretch" }, style], children: [
4165
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_react_native31.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
4253
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_react_native32.View, { style: [{ alignSelf: "stretch" }, style], children: [
4254
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_react_native32.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
4166
4255
  /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Avatar, { uri: avatarUri, name: creatorName, size: 24, style: { marginRight: theme.spacing.sm } }),
4167
- /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_react_native31.View, { style: { flexDirection: "row", alignItems: "center", flex: 1, minWidth: 0, marginRight: theme.spacing.sm }, children: [
4256
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_react_native32.View, { style: { flexDirection: "row", alignItems: "center", flex: 1, minWidth: 0, marginRight: theme.spacing.sm }, children: [
4168
4257
  /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4169
4258
  Text,
4170
4259
  {
@@ -4179,9 +4268,9 @@ function PreviewMetaRow({
4179
4268
  children: title
4180
4269
  }
4181
4270
  ),
4182
- tag ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react_native31.View, { style: { marginLeft: theme.spacing.sm }, children: tag }) : null
4271
+ tag ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react_native32.View, { style: { marginLeft: theme.spacing.sm }, children: tag }) : null
4183
4272
  ] }),
4184
- rightMetric ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react_native31.View, { children: rightMetric }) : null
4273
+ rightMetric ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react_native32.View, { children: rightMetric }) : null
4185
4274
  ] }),
4186
4275
  subtitle ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4187
4276
  Text,
@@ -4237,9 +4326,9 @@ function PreviewMetaSection({ app, isOwner, creator, downloadsCount }) {
4237
4326
  subtitle: app.description,
4238
4327
  avatarUri: (creator == null ? void 0 : creator.avatar) ?? null,
4239
4328
  creatorName: (creator == null ? void 0 : creator.name) ?? null,
4240
- tag: isOwner || app.forkedFromAppId ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react_native32.View, { style: { paddingHorizontal: 8, paddingVertical: 2, borderRadius: 999, backgroundColor: "#3700B3" }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Text, { variant: "caption", style: { color: "#fff", fontWeight: theme.typography.fontWeight.semibold }, children: app.forkedFromAppId ? "Remix" : "Owner" }) }) : null,
4329
+ tag: isOwner || app.forkedFromAppId ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(import_react_native33.View, { style: { paddingHorizontal: 8, paddingVertical: 2, borderRadius: 999, backgroundColor: "#3700B3" }, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Text, { variant: "caption", style: { color: "#fff", fontWeight: theme.typography.fontWeight.semibold }, children: app.forkedFromAppId ? "Remix" : "Owner" }) }) : null,
4241
4330
  rightMetric: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
4242
- import_react_native32.View,
4331
+ import_react_native33.View,
4243
4332
  {
4244
4333
  style: {
4245
4334
  flexDirection: "row",
@@ -4273,10 +4362,10 @@ function PreviewMetaSection({ app, isOwner, creator, downloadsCount }) {
4273
4362
  }
4274
4363
 
4275
4364
  // src/studio/ui/preview-panel/PreviewCustomizeSection.tsx
4276
- var import_react_native34 = require("react-native");
4365
+ var import_react_native35 = require("react-native");
4277
4366
 
4278
4367
  // src/studio/ui/preview-panel/PressableCardRow.tsx
4279
- var import_react_native33 = require("react-native");
4368
+ var import_react_native34 = require("react-native");
4280
4369
  var import_jsx_runtime34 = require("react/jsx-runtime");
4281
4370
  function PressableCardRow({
4282
4371
  accessibilityLabel,
@@ -4289,20 +4378,20 @@ function PressableCardRow({
4289
4378
  style
4290
4379
  }) {
4291
4380
  return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4292
- import_react_native33.Pressable,
4381
+ import_react_native34.Pressable,
4293
4382
  {
4294
4383
  accessibilityRole: "button",
4295
4384
  accessibilityLabel,
4296
4385
  disabled,
4297
4386
  onPress,
4298
4387
  style: ({ pressed }) => ({ opacity: disabled ? 0.6 : pressed ? 0.85 : 1 }),
4299
- children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Card, { padded: false, border: false, style, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react_native33.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
4388
+ children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Card, { padded: false, border: false, style, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react_native34.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
4300
4389
  left,
4301
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react_native33.View, { style: { flex: 1, minWidth: 0 }, children: [
4390
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react_native34.View, { style: { flex: 1, minWidth: 0 }, children: [
4302
4391
  title,
4303
4392
  subtitle ? subtitle : null
4304
4393
  ] }),
4305
- right ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_native33.View, { style: { marginLeft: 16 }, children: right }) : null
4394
+ right ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_native34.View, { style: { marginLeft: 16 }, children: right }) : null
4306
4395
  ] }) })
4307
4396
  }
4308
4397
  );
@@ -4344,7 +4433,7 @@ function PreviewCustomizeSection({
4344
4433
  return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
4345
4434
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(SectionTitle, { children: "Customize" }),
4346
4435
  showProcessing ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
4347
- import_react_native34.View,
4436
+ import_react_native35.View,
4348
4437
  {
4349
4438
  style: {
4350
4439
  flexDirection: "row",
@@ -4358,7 +4447,7 @@ function PreviewCustomizeSection({
4358
4447
  },
4359
4448
  children: [
4360
4449
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4361
- import_react_native34.View,
4450
+ import_react_native35.View,
4362
4451
  {
4363
4452
  style: {
4364
4453
  width: 40,
@@ -4369,10 +4458,10 @@ function PreviewCustomizeSection({
4369
4458
  backgroundColor: withAlpha(theme.colors.warning, 0.1),
4370
4459
  marginRight: theme.spacing.lg
4371
4460
  },
4372
- children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_native34.ActivityIndicator, { color: theme.colors.warning, size: "small" })
4461
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_native35.ActivityIndicator, { color: theme.colors.warning, size: "small" })
4373
4462
  }
4374
4463
  ),
4375
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_react_native34.View, { style: { flex: 1, minWidth: 0 }, children: [
4464
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_react_native35.View, { style: { flex: 1, minWidth: 0 }, children: [
4376
4465
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Text, { style: { color: theme.colors.text, fontSize: 16, lineHeight: 20, fontWeight: theme.typography.fontWeight.semibold }, children: app.status === "error" ? "Error" : "Processing" }),
4377
4466
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Text, { style: { color: theme.colors.textMuted, fontSize: 12, lineHeight: 16, marginTop: 2 }, children: statusDescription(app.status, app.statusError) })
4378
4467
  ] })
@@ -4393,7 +4482,7 @@ function PreviewCustomizeSection({
4393
4482
  marginBottom: theme.spacing.sm
4394
4483
  },
4395
4484
  left: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4396
- import_react_native34.View,
4485
+ import_react_native35.View,
4397
4486
  {
4398
4487
  style: {
4399
4488
  width: 40,
@@ -4426,7 +4515,7 @@ function PreviewCustomizeSection({
4426
4515
  marginBottom: theme.spacing.sm
4427
4516
  },
4428
4517
  left: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4429
- import_react_native34.View,
4518
+ import_react_native35.View,
4430
4519
  {
4431
4520
  style: {
4432
4521
  width: 40,
@@ -4450,16 +4539,16 @@ function PreviewCustomizeSection({
4450
4539
 
4451
4540
  // src/studio/ui/preview-panel/PreviewCollaborateSection.tsx
4452
4541
  var React29 = __toESM(require("react"));
4453
- var import_react_native40 = require("react-native");
4542
+ var import_react_native41 = require("react-native");
4454
4543
  var import_lucide_react_native9 = require("lucide-react-native");
4455
4544
 
4456
4545
  // src/components/merge-requests/MergeRequestStatusCard.tsx
4457
4546
  var React25 = __toESM(require("react"));
4458
- var import_react_native36 = require("react-native");
4547
+ var import_react_native37 = require("react-native");
4459
4548
  var import_lucide_react_native7 = require("lucide-react-native");
4460
4549
 
4461
4550
  // src/components/primitives/MarkdownText.tsx
4462
- var import_react_native35 = require("react-native");
4551
+ var import_react_native36 = require("react-native");
4463
4552
  var import_react_native_markdown_display = __toESM(require("react-native-markdown-display"));
4464
4553
  var import_jsx_runtime37 = require("react/jsx-runtime");
4465
4554
  function MarkdownText({ markdown, variant = "chat", bodyColor, style }) {
@@ -4472,7 +4561,7 @@ function MarkdownText({ markdown, variant = "chat", bodyColor, style }) {
4472
4561
  const codeTextColor = isDark ? "#FFFFFF" : theme.colors.text;
4473
4562
  const paragraphBottom = variant === "mergeRequest" ? 8 : 6;
4474
4563
  const baseLineHeight = variant === "mergeRequest" ? 22 : 20;
4475
- return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_react_native35.View, { style, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4564
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(import_react_native36.View, { style, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4476
4565
  import_react_native_markdown_display.default,
4477
4566
  {
4478
4567
  style: {
@@ -4485,7 +4574,7 @@ function MarkdownText({ markdown, variant = "chat", bodyColor, style }) {
4485
4574
  paddingHorizontal: variant === "mergeRequest" ? 6 : 4,
4486
4575
  paddingVertical: variant === "mergeRequest" ? 2 : 0,
4487
4576
  borderRadius: variant === "mergeRequest" ? 6 : 4,
4488
- fontFamily: import_react_native35.Platform.OS === "ios" ? "Menlo" : "monospace",
4577
+ fontFamily: import_react_native36.Platform.OS === "ios" ? "Menlo" : "monospace",
4489
4578
  fontSize: 13
4490
4579
  },
4491
4580
  code_block: {
@@ -4597,15 +4686,15 @@ function MergeRequestStatusCard({
4597
4686
  const createdIso = toIsoString(mergeRequest.createdAt ?? null);
4598
4687
  const headerTimeAgo = updatedIso ? formatTimeAgo(updatedIso) : "";
4599
4688
  const createdTimeAgo = createdIso ? formatTimeAgo(createdIso) : "";
4600
- const rotate = React25.useRef(new import_react_native36.Animated.Value(expanded ? 1 : 0)).current;
4689
+ const rotate = React25.useRef(new import_react_native37.Animated.Value(expanded ? 1 : 0)).current;
4601
4690
  React25.useEffect(() => {
4602
- import_react_native36.Animated.timing(rotate, {
4691
+ import_react_native37.Animated.timing(rotate, {
4603
4692
  toValue: expanded ? 1 : 0,
4604
4693
  duration: 200,
4605
4694
  useNativeDriver: true
4606
4695
  }).start();
4607
4696
  }, [expanded, rotate]);
4608
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_native36.Pressable, { onPress: () => setExpanded(!expanded), style: ({ pressed }) => [{ opacity: pressed ? 0.95 : 1 }], children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
4697
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_native37.Pressable, { onPress: () => setExpanded(!expanded), style: ({ pressed }) => [{ opacity: pressed ? 0.95 : 1 }], children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
4609
4698
  Card,
4610
4699
  {
4611
4700
  padded: false,
@@ -4618,10 +4707,10 @@ function MergeRequestStatusCard({
4618
4707
  style
4619
4708
  ],
4620
4709
  children: [
4621
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react_native36.View, { style: { flexDirection: "row", alignItems: "center", gap: theme.spacing.lg }, children: [
4622
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_native36.View, { style: { width: 40, height: 40, borderRadius: 999, alignItems: "center", justifyContent: "center", backgroundColor: bgColor }, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(StatusIcon, { size: 20, color: iconColor }) }),
4623
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react_native36.View, { style: { flex: 1, minWidth: 0 }, children: [
4624
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react_native36.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, children: [
4710
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react_native37.View, { style: { flexDirection: "row", alignItems: "center", gap: theme.spacing.lg }, children: [
4711
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_native37.View, { style: { width: 40, height: 40, borderRadius: 999, alignItems: "center", justifyContent: "center", backgroundColor: bgColor }, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(StatusIcon, { size: 20, color: iconColor }) }),
4712
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react_native37.View, { style: { flex: 1, minWidth: 0 }, children: [
4713
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react_native37.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, children: [
4625
4714
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4626
4715
  Text,
4627
4716
  {
@@ -4640,8 +4729,8 @@ function MergeRequestStatusCard({
4640
4729
  ] }),
4641
4730
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { style: { fontSize: 12, lineHeight: 16, color: theme.colors.textMuted }, numberOfLines: 1, children: mergeRequest.title ?? "Untitled merge request" })
4642
4731
  ] }),
4643
- headerRight ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_native36.View, { children: headerRight }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4644
- import_react_native36.Animated.View,
4732
+ headerRight ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_native37.View, { children: headerRight }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4733
+ import_react_native37.Animated.View,
4645
4734
  {
4646
4735
  style: {
4647
4736
  transform: [
@@ -4654,7 +4743,7 @@ function MergeRequestStatusCard({
4654
4743
  }
4655
4744
  )
4656
4745
  ] }),
4657
- expanded ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react_native36.View, { style: { marginTop: 16, marginLeft: 56 }, children: [
4746
+ expanded ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react_native37.View, { style: { marginTop: 16, marginLeft: 56 }, children: [
4658
4747
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4659
4748
  Text,
4660
4749
  {
@@ -4690,16 +4779,16 @@ function MergeRequestStatusCard({
4690
4779
 
4691
4780
  // src/components/merge-requests/ReviewMergeRequestCarousel.tsx
4692
4781
  var React28 = __toESM(require("react"));
4693
- var import_react_native39 = require("react-native");
4782
+ var import_react_native40 = require("react-native");
4694
4783
 
4695
4784
  // src/components/merge-requests/ReviewMergeRequestCard.tsx
4696
4785
  var React27 = __toESM(require("react"));
4697
- var import_react_native38 = require("react-native");
4786
+ var import_react_native39 = require("react-native");
4698
4787
  var import_lucide_react_native8 = require("lucide-react-native");
4699
4788
 
4700
4789
  // src/components/merge-requests/ReviewMergeRequestActionButton.tsx
4701
4790
  var React26 = __toESM(require("react"));
4702
- var import_react_native37 = require("react-native");
4791
+ var import_react_native38 = require("react-native");
4703
4792
  var import_jsx_runtime39 = require("react/jsx-runtime");
4704
4793
  function ReviewMergeRequestActionButton({
4705
4794
  accessibilityLabel,
@@ -4716,7 +4805,7 @@ function ReviewMergeRequestActionButton({
4716
4805
  const paddingVertical = iconOnly ? 0 : 8;
4717
4806
  const opacity = disabled ? 0.5 : pressed ? 0.9 : 1;
4718
4807
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4719
- import_react_native37.View,
4808
+ import_react_native38.View,
4720
4809
  {
4721
4810
  style: {
4722
4811
  width,
@@ -4731,7 +4820,7 @@ function ReviewMergeRequestActionButton({
4731
4820
  justifyContent: "center"
4732
4821
  },
4733
4822
  children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4734
- import_react_native37.Pressable,
4823
+ import_react_native38.Pressable,
4735
4824
  {
4736
4825
  accessibilityRole: "button",
4737
4826
  accessibilityLabel,
@@ -4773,12 +4862,12 @@ function ReviewMergeRequestCard({
4773
4862
  const theme = useTheme();
4774
4863
  const status = React27.useMemo(() => getMergeRequestStatusDisplay(mr.status), [mr.status]);
4775
4864
  const canAct = mr.status === "open";
4776
- const rotate = React27.useRef(new import_react_native38.Animated.Value(isExpanded ? 1 : 0)).current;
4865
+ const rotate = React27.useRef(new import_react_native39.Animated.Value(isExpanded ? 1 : 0)).current;
4777
4866
  React27.useEffect(() => {
4778
- import_react_native38.Animated.timing(rotate, { toValue: isExpanded ? 1 : 0, duration: 200, useNativeDriver: true }).start();
4867
+ import_react_native39.Animated.timing(rotate, { toValue: isExpanded ? 1 : 0, duration: 200, useNativeDriver: true }).start();
4779
4868
  }, [isExpanded, rotate]);
4780
4869
  const position = total > 1 ? `${index + 1}/${total}` : "Merge request";
4781
- return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_native38.Pressable, { onPress: onToggle, style: ({ pressed }) => ({ opacity: pressed ? 0.95 : 1 }), children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
4870
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_native39.Pressable, { onPress: onToggle, style: ({ pressed }) => ({ opacity: pressed ? 0.95 : 1 }), children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
4782
4871
  Card,
4783
4872
  {
4784
4873
  padded: false,
@@ -4791,9 +4880,9 @@ function ReviewMergeRequestCard({
4791
4880
  }
4792
4881
  ],
4793
4882
  children: [
4794
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", gap: 12 }, children: [
4883
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native39.View, { style: { flexDirection: "row", alignItems: "center", gap: 12 }, children: [
4795
4884
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Avatar, { size: 40, uri: (creator == null ? void 0 : creator.avatar) ?? null, name: (creator == null ? void 0 : creator.name) ?? void 0 }),
4796
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flex: 1, minWidth: 0 }, children: [
4885
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native39.View, { style: { flex: 1, minWidth: 0 }, children: [
4797
4886
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4798
4887
  Text,
4799
4888
  {
@@ -4809,7 +4898,7 @@ function ReviewMergeRequestCard({
4809
4898
  ] })
4810
4899
  ] }),
4811
4900
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4812
- import_react_native38.Animated.View,
4901
+ import_react_native39.Animated.View,
4813
4902
  {
4814
4903
  style: {
4815
4904
  transform: [{ rotate: rotate.interpolate({ inputRange: [0, 1], outputRange: ["0deg", "180deg"] }) }]
@@ -4818,7 +4907,7 @@ function ReviewMergeRequestCard({
4818
4907
  }
4819
4908
  )
4820
4909
  ] }),
4821
- isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { marginTop: 16 }, children: [
4910
+ isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native39.View, { style: { marginTop: 16 }, children: [
4822
4911
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4823
4912
  Text,
4824
4913
  {
@@ -4836,9 +4925,9 @@ function ReviewMergeRequestCard({
4836
4925
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { color: theme.colors.textMuted, fontSize: 12, lineHeight: 16, marginBottom: 12 }, children: creator ? `${creator.approvedOpenedMergeRequests} approved merge${creator.approvedOpenedMergeRequests !== 1 ? "s" : ""}` : "Loading stats..." }),
4837
4926
  mr.description ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(MarkdownText, { markdown: mr.description, variant: "mergeRequest" }) : null
4838
4927
  ] }) : null,
4839
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_native38.View, { style: { height: 1, backgroundColor: withAlpha(theme.colors.borderStrong, 0.5), marginTop: 12, marginBottom: 12 } }),
4840
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, children: [
4841
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flexDirection: "row", gap: 8 }, children: [
4928
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_native39.View, { style: { height: 1, backgroundColor: withAlpha(theme.colors.borderStrong, 0.5), marginTop: 12, marginBottom: 12 } }),
4929
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native39.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, children: [
4930
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native39.View, { style: { flexDirection: "row", gap: 8 }, children: [
4842
4931
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4843
4932
  ReviewMergeRequestActionButton,
4844
4933
  {
@@ -4847,7 +4936,7 @@ function ReviewMergeRequestCard({
4847
4936
  disabled: !canAct || isAnyProcessing,
4848
4937
  onPress: onReject,
4849
4938
  iconOnly: !isExpanded,
4850
- children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
4939
+ children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native39.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
4851
4940
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react_native8.X, { size: 18, color: "#FFFFFF" }),
4852
4941
  isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 13, color: "#FFFFFF", fontWeight: theme.typography.fontWeight.semibold }, children: "Reject" }) : null
4853
4942
  ] })
@@ -4861,10 +4950,10 @@ function ReviewMergeRequestCard({
4861
4950
  disabled: !canAct || isAnyProcessing,
4862
4951
  onPress: onApprove,
4863
4952
  iconOnly: !isExpanded,
4864
- children: isProcessing ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
4865
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_native38.ActivityIndicator, { size: "small", color: "#FFFFFF" }),
4953
+ children: isProcessing ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native39.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
4954
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_native39.ActivityIndicator, { size: "small", color: "#FFFFFF" }),
4866
4955
  isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 13, color: "#FFFFFF", fontWeight: theme.typography.fontWeight.semibold }, children: "Processing" }) : null
4867
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
4956
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native39.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
4868
4957
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react_native8.Check, { size: 18, color: "#FFFFFF" }),
4869
4958
  isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 13, color: "#FFFFFF", fontWeight: theme.typography.fontWeight.semibold }, children: "Approve" }) : null
4870
4959
  ] })
@@ -4879,7 +4968,7 @@ function ReviewMergeRequestCard({
4879
4968
  disabled: isBuilding || isTestingThis,
4880
4969
  onPress: onTest,
4881
4970
  iconOnly: !isExpanded,
4882
- children: isTestingThis ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_native38.ActivityIndicator, { size: "small", color: "#888" }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
4971
+ children: isTestingThis ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_native39.ActivityIndicator, { size: "small", color: "#888" }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native39.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
4883
4972
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react_native8.Play, { size: 14, color: theme.colors.text }),
4884
4973
  isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 13, color: theme.colors.text, fontWeight: theme.typography.fontWeight.semibold }, children: "Test" }) : null
4885
4974
  ] })
@@ -4905,32 +4994,32 @@ function ReviewMergeRequestCarousel({
4905
4994
  style
4906
4995
  }) {
4907
4996
  const theme = useTheme();
4908
- const { width } = (0, import_react_native39.useWindowDimensions)();
4997
+ const { width } = (0, import_react_native40.useWindowDimensions)();
4909
4998
  const [expanded, setExpanded] = React28.useState({});
4910
- const carouselScrollX = React28.useRef(new import_react_native39.Animated.Value(0)).current;
4999
+ const carouselScrollX = React28.useRef(new import_react_native40.Animated.Value(0)).current;
4911
5000
  const peekAmount = 24;
4912
5001
  const gap = 16;
4913
5002
  const cardWidth = React28.useMemo(() => Math.max(1, width - theme.spacing.lg * 2 - peekAmount), [peekAmount, theme.spacing.lg, width]);
4914
5003
  const snapInterval = cardWidth + gap;
4915
5004
  const dotColor = theme.scheme === "dark" ? "#FFFFFF" : "#000000";
4916
5005
  if (mergeRequests.length === 0) return null;
4917
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_react_native39.View, { style: [{ marginHorizontal: -theme.spacing.lg }, style], children: [
5006
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_react_native40.View, { style: [{ marginHorizontal: -theme.spacing.lg }, style], children: [
4918
5007
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
4919
- import_react_native39.FlatList,
5008
+ import_react_native40.FlatList,
4920
5009
  {
4921
5010
  horizontal: true,
4922
5011
  data: mergeRequests,
4923
5012
  keyExtractor: (mr) => mr.id,
4924
5013
  showsHorizontalScrollIndicator: false,
4925
5014
  contentContainerStyle: { paddingHorizontal: theme.spacing.lg, paddingVertical: theme.spacing.sm },
4926
- ItemSeparatorComponent: () => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_native39.View, { style: { width: gap } }),
5015
+ ItemSeparatorComponent: () => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_native40.View, { style: { width: gap } }),
4927
5016
  snapToAlignment: "start",
4928
5017
  decelerationRate: "fast",
4929
5018
  snapToInterval: snapInterval,
4930
5019
  disableIntervalMomentum: true,
4931
5020
  style: { paddingRight: peekAmount },
4932
- ListFooterComponent: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_native39.View, { style: { width: peekAmount } }),
4933
- onScroll: import_react_native39.Animated.event([{ nativeEvent: { contentOffset: { x: carouselScrollX } } }], {
5021
+ ListFooterComponent: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_native40.View, { style: { width: peekAmount } }),
5022
+ onScroll: import_react_native40.Animated.event([{ nativeEvent: { contentOffset: { x: carouselScrollX } } }], {
4934
5023
  useNativeDriver: false
4935
5024
  }),
4936
5025
  scrollEventThrottle: 16,
@@ -4941,7 +5030,7 @@ function ReviewMergeRequestCarousel({
4941
5030
  const isProcessing = Boolean(processingMrId && processingMrId === item.id);
4942
5031
  const isAnyProcessing = Boolean(processingMrId);
4943
5032
  const isTestingThis = Boolean(testingMrId && testingMrId === item.id);
4944
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_native39.View, { style: { width: cardWidth }, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
5033
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_native40.View, { style: { width: cardWidth }, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
4945
5034
  ReviewMergeRequestCard,
4946
5035
  {
4947
5036
  mr: item,
@@ -4962,7 +5051,7 @@ function ReviewMergeRequestCarousel({
4962
5051
  }
4963
5052
  }
4964
5053
  ),
4965
- mergeRequests.length >= 1 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_native39.View, { style: { flexDirection: "row", justifyContent: "center", columnGap: 8, marginTop: theme.spacing.md }, children: mergeRequests.map((mr, index) => {
5054
+ mergeRequests.length >= 1 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_native40.View, { style: { flexDirection: "row", justifyContent: "center", columnGap: 8, marginTop: theme.spacing.md }, children: mergeRequests.map((mr, index) => {
4966
5055
  const inputRange = [(index - 1) * snapInterval, index * snapInterval, (index + 1) * snapInterval];
4967
5056
  const scale = carouselScrollX.interpolate({
4968
5057
  inputRange,
@@ -4975,7 +5064,7 @@ function ReviewMergeRequestCarousel({
4975
5064
  extrapolate: "clamp"
4976
5065
  });
4977
5066
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
4978
- import_react_native39.Animated.View,
5067
+ import_react_native40.Animated.View,
4979
5068
  {
4980
5069
  style: {
4981
5070
  width: 8,
@@ -5036,7 +5125,7 @@ function PreviewCollaborateSection({
5036
5125
  accessibilityLabel: "Submit merge request",
5037
5126
  disabled: submittingMr,
5038
5127
  onPress: () => {
5039
- import_react_native40.Alert.alert(
5128
+ import_react_native41.Alert.alert(
5040
5129
  "Submit Merge Request",
5041
5130
  "Are you sure you want to submit your changes to the original app?",
5042
5131
  [
@@ -5062,7 +5151,7 @@ function PreviewCollaborateSection({
5062
5151
  marginBottom: theme.spacing.sm
5063
5152
  },
5064
5153
  left: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5065
- import_react_native40.View,
5154
+ import_react_native41.View,
5066
5155
  {
5067
5156
  style: {
5068
5157
  width: 40,
@@ -5073,7 +5162,7 @@ function PreviewCollaborateSection({
5073
5162
  backgroundColor: withAlpha("#03DAC6", 0.1),
5074
5163
  marginRight: theme.spacing.lg
5075
5164
  },
5076
- children: submittingMr ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_native40.ActivityIndicator, { color: "#03DAC6", size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(MergeIcon, { width: 20, height: 20, color: "#03DAC6" })
5165
+ children: submittingMr ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_native41.ActivityIndicator, { color: "#03DAC6", size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(MergeIcon, { width: 20, height: 20, color: "#03DAC6" })
5077
5166
  }
5078
5167
  ),
5079
5168
  title: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Text, { style: { color: theme.colors.text, fontSize: 16, lineHeight: 20, fontWeight: theme.typography.fontWeight.semibold }, children: "Submit your new changes" }),
@@ -5111,7 +5200,7 @@ function PreviewCollaborateSection({
5111
5200
  children: "History"
5112
5201
  }
5113
5202
  ),
5114
- outgoingMergeRequests.map((mr) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_native40.View, { style: { marginBottom: theme.spacing.sm }, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(MergeRequestStatusCard, { mergeRequest: toMergeRequestSummary(mr) }) }, mr.id))
5203
+ outgoingMergeRequests.map((mr) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_native41.View, { style: { marginBottom: theme.spacing.sm }, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(MergeRequestStatusCard, { mergeRequest: toMergeRequestSummary(mr) }) }, mr.id))
5115
5204
  ] }) : null
5116
5205
  ] });
5117
5206
  }
@@ -5435,9 +5524,9 @@ function PreviewPanel({
5435
5524
  });
5436
5525
  const header = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(PreviewPanelHeader, { isOwner, onClose, onNavigateHome, onGoToChat });
5437
5526
  if (loading || !app) {
5438
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(PreviewPage, { header, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_react_native41.View, { style: { flex: 1, justifyContent: "center", alignItems: "center", padding: 24 }, children: [
5439
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_react_native41.ActivityIndicator, {}),
5440
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_react_native41.View, { style: { height: 12 } }),
5527
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(PreviewPage, { header, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_react_native42.View, { style: { flex: 1, justifyContent: "center", alignItems: "center", padding: 24 }, children: [
5528
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_react_native42.ActivityIndicator, {}),
5529
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_react_native42.View, { style: { height: 12 } }),
5441
5530
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { variant: "bodyMuted", children: "Loading app\u2026" })
5442
5531
  ] }) });
5443
5532
  }
@@ -5494,21 +5583,20 @@ function PreviewPanel({
5494
5583
 
5495
5584
  // src/studio/ui/ChatPanel.tsx
5496
5585
  var React36 = __toESM(require("react"));
5497
- var import_react_native49 = require("react-native");
5586
+ var import_react_native50 = require("react-native");
5498
5587
 
5499
5588
  // src/components/chat/ChatPage.tsx
5500
5589
  var React34 = __toESM(require("react"));
5501
- var import_react_native45 = require("react-native");
5590
+ var import_react_native46 = require("react-native");
5502
5591
  var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
5503
- var import_react_native_reanimated2 = __toESM(require("react-native-reanimated"));
5504
5592
 
5505
5593
  // src/components/chat/ChatMessageList.tsx
5506
5594
  var React33 = __toESM(require("react"));
5507
- var import_react_native44 = require("react-native");
5595
+ var import_react_native45 = require("react-native");
5508
5596
  var import_bottom_sheet5 = require("@gorhom/bottom-sheet");
5509
5597
 
5510
5598
  // src/components/chat/ChatMessageBubble.tsx
5511
- var import_react_native42 = require("react-native");
5599
+ var import_react_native43 = require("react-native");
5512
5600
  var import_lucide_react_native10 = require("lucide-react-native");
5513
5601
  var import_jsx_runtime44 = require("react/jsx-runtime");
5514
5602
  function ChatMessageBubble({ message, renderContent, style }) {
@@ -5524,7 +5612,7 @@ function ChatMessageBubble({ message, renderContent, style }) {
5524
5612
  const bubbleVariant = isHuman ? "surface" : "surfaceRaised";
5525
5613
  const cornerStyle = isHuman ? { borderTopRightRadius: 0 } : { borderTopLeftRadius: 0 };
5526
5614
  const bodyColor = metaStatus === "success" ? theme.colors.success : metaStatus === "error" ? theme.colors.danger : void 0;
5527
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native42.View, { style: [align, style], children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
5615
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native43.View, { style: [align, style], children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
5528
5616
  Surface,
5529
5617
  {
5530
5618
  variant: bubbleVariant,
@@ -5539,10 +5627,10 @@ function ChatMessageBubble({ message, renderContent, style }) {
5539
5627
  },
5540
5628
  cornerStyle
5541
5629
  ],
5542
- children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_react_native42.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
5630
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_react_native43.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
5543
5631
  isMergeCompleted ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react_native10.CheckCheck, { size: 16, color: theme.colors.success, style: { marginRight: theme.spacing.sm } }) : null,
5544
5632
  isMergeApproved ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react_native10.GitMerge, { size: 16, color: theme.colors.text, style: { marginRight: theme.spacing.sm } }) : null,
5545
- /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native42.View, { style: { flexShrink: 1, minWidth: 0 }, children: renderContent ? renderContent(message) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(MarkdownText, { markdown: message.content, variant: "chat", bodyColor }) })
5633
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native43.View, { style: { flexShrink: 1, minWidth: 0 }, children: renderContent ? renderContent(message) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(MarkdownText, { markdown: message.content, variant: "chat", bodyColor }) })
5546
5634
  ] })
5547
5635
  }
5548
5636
  ) });
@@ -5550,23 +5638,23 @@ function ChatMessageBubble({ message, renderContent, style }) {
5550
5638
 
5551
5639
  // src/components/chat/TypingIndicator.tsx
5552
5640
  var React32 = __toESM(require("react"));
5553
- var import_react_native43 = require("react-native");
5641
+ var import_react_native44 = require("react-native");
5554
5642
  var import_jsx_runtime45 = require("react/jsx-runtime");
5555
5643
  function TypingIndicator({ style }) {
5556
5644
  const theme = useTheme();
5557
5645
  const dotColor = theme.colors.textSubtle;
5558
5646
  const anims = React32.useMemo(
5559
- () => [new import_react_native43.Animated.Value(0.3), new import_react_native43.Animated.Value(0.3), new import_react_native43.Animated.Value(0.3)],
5647
+ () => [new import_react_native44.Animated.Value(0.3), new import_react_native44.Animated.Value(0.3), new import_react_native44.Animated.Value(0.3)],
5560
5648
  []
5561
5649
  );
5562
5650
  React32.useEffect(() => {
5563
5651
  const loops = [];
5564
5652
  anims.forEach((a, idx) => {
5565
- const seq = import_react_native43.Animated.sequence([
5566
- import_react_native43.Animated.timing(a, { toValue: 1, duration: 420, useNativeDriver: true, delay: idx * 140 }),
5567
- import_react_native43.Animated.timing(a, { toValue: 0.3, duration: 420, useNativeDriver: true })
5653
+ const seq = import_react_native44.Animated.sequence([
5654
+ import_react_native44.Animated.timing(a, { toValue: 1, duration: 420, useNativeDriver: true, delay: idx * 140 }),
5655
+ import_react_native44.Animated.timing(a, { toValue: 0.3, duration: 420, useNativeDriver: true })
5568
5656
  ]);
5569
- const loop = import_react_native43.Animated.loop(seq);
5657
+ const loop = import_react_native44.Animated.loop(seq);
5570
5658
  loops.push(loop);
5571
5659
  loop.start();
5572
5660
  });
@@ -5574,8 +5662,8 @@ function TypingIndicator({ style }) {
5574
5662
  loops.forEach((l) => l.stop());
5575
5663
  };
5576
5664
  }, [anims]);
5577
- return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_react_native43.View, { style: [{ flexDirection: "row", alignItems: "center" }, style], children: anims.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
5578
- import_react_native43.Animated.View,
5665
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_react_native44.View, { style: [{ flexDirection: "row", alignItems: "center" }, style], children: anims.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
5666
+ import_react_native44.Animated.View,
5579
5667
  {
5580
5668
  style: {
5581
5669
  width: 8,
@@ -5584,7 +5672,7 @@ function TypingIndicator({ style }) {
5584
5672
  marginHorizontal: 3,
5585
5673
  backgroundColor: dotColor,
5586
5674
  opacity: a,
5587
- transform: [{ translateY: import_react_native43.Animated.multiply(import_react_native43.Animated.subtract(a, 0.3), 2) }]
5675
+ transform: [{ translateY: import_react_native44.Animated.multiply(import_react_native44.Animated.subtract(a, 0.3), 2) }]
5588
5676
  }
5589
5677
  },
5590
5678
  i
@@ -5608,19 +5696,19 @@ var ChatMessageList = React33.forwardRef(
5608
5696
  const nearBottomRef = React33.useRef(true);
5609
5697
  const initialScrollDoneRef = React33.useRef(false);
5610
5698
  const lastMessageIdRef = React33.useRef(null);
5699
+ const data = React33.useMemo(() => {
5700
+ return [...messages].reverse();
5701
+ }, [messages]);
5611
5702
  const scrollToBottom = React33.useCallback((options) => {
5612
5703
  var _a;
5613
5704
  const animated = (options == null ? void 0 : options.animated) ?? true;
5614
- (_a = listRef.current) == null ? void 0 : _a.scrollToEnd({ animated });
5705
+ (_a = listRef.current) == null ? void 0 : _a.scrollToOffset({ offset: 0, animated });
5615
5706
  }, []);
5616
5707
  React33.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
5617
5708
  const handleScroll = React33.useCallback(
5618
5709
  (e) => {
5619
5710
  const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;
5620
- const distanceFromBottom = Math.max(
5621
- contentSize.height - Math.max(bottomInset, 0) - (contentOffset.y + layoutMeasurement.height),
5622
- 0
5623
- );
5711
+ const distanceFromBottom = Math.max(contentOffset.y - Math.max(bottomInset, 0), 0);
5624
5712
  const isNear = distanceFromBottom <= nearBottomThreshold;
5625
5713
  if (nearBottomRef.current !== isNear) {
5626
5714
  nearBottomRef.current = isNear;
@@ -5629,15 +5717,6 @@ var ChatMessageList = React33.forwardRef(
5629
5717
  },
5630
5718
  [bottomInset, nearBottomThreshold, onNearBottomChange]
5631
5719
  );
5632
- React33.useEffect(() => {
5633
- var _a;
5634
- if (initialScrollDoneRef.current) return;
5635
- if (messages.length === 0) return;
5636
- initialScrollDoneRef.current = true;
5637
- lastMessageIdRef.current = ((_a = messages[messages.length - 1]) == null ? void 0 : _a.id) ?? null;
5638
- const id = requestAnimationFrame(() => scrollToBottom({ animated: false }));
5639
- return () => cancelAnimationFrame(id);
5640
- }, [messages, scrollToBottom]);
5641
5720
  React33.useEffect(() => {
5642
5721
  if (!initialScrollDoneRef.current) return;
5643
5722
  const lastId = messages.length > 0 ? messages[messages.length - 1].id : null;
@@ -5655,33 +5734,37 @@ var ChatMessageList = React33.forwardRef(
5655
5734
  }
5656
5735
  return void 0;
5657
5736
  }, [showTypingIndicator, scrollToBottom]);
5658
- React33.useEffect(() => {
5659
- if (!initialScrollDoneRef.current) return;
5660
- if (!nearBottomRef.current) return;
5661
- const id = requestAnimationFrame(() => scrollToBottom({ animated: false }));
5662
- return () => cancelAnimationFrame(id);
5663
- }, [bottomInset, scrollToBottom]);
5664
5737
  return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
5665
5738
  import_bottom_sheet5.BottomSheetFlatList,
5666
5739
  {
5667
5740
  ref: listRef,
5668
- data: messages,
5741
+ inverted: true,
5742
+ data,
5669
5743
  keyExtractor: (m) => m.id,
5744
+ keyboardShouldPersistTaps: "handled",
5670
5745
  onScroll: handleScroll,
5671
5746
  scrollEventThrottle: 16,
5672
5747
  showsVerticalScrollIndicator: false,
5748
+ onContentSizeChange: () => {
5749
+ if (initialScrollDoneRef.current) return;
5750
+ initialScrollDoneRef.current = true;
5751
+ lastMessageIdRef.current = messages.length > 0 ? messages[messages.length - 1].id : null;
5752
+ nearBottomRef.current = true;
5753
+ onNearBottomChange == null ? void 0 : onNearBottomChange(true);
5754
+ requestAnimationFrame(() => scrollToBottom({ animated: false }));
5755
+ },
5673
5756
  contentContainerStyle: [
5674
5757
  {
5675
5758
  paddingHorizontal: theme.spacing.lg,
5676
- paddingTop: theme.spacing.sm,
5677
- paddingBottom: theme.spacing.sm
5759
+ paddingVertical: theme.spacing.sm
5678
5760
  },
5679
5761
  contentStyle
5680
5762
  ],
5681
- renderItem: ({ item, index }) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react_native44.View, { style: { marginTop: index === 0 ? 0 : theme.spacing.sm }, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ChatMessageBubble, { message: item, renderContent: renderMessageContent }) }),
5682
- ListFooterComponent: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_react_native44.View, { children: [
5683
- showTypingIndicator ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react_native44.View, { style: { marginTop: theme.spacing.sm, alignSelf: "flex-start", paddingHorizontal: theme.spacing.lg }, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(TypingIndicator, {}) }) : null,
5684
- bottomInset > 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react_native44.View, { style: { height: bottomInset } }) : null
5763
+ ItemSeparatorComponent: () => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react_native45.View, { style: { height: theme.spacing.sm } }),
5764
+ renderItem: ({ item }) => /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(ChatMessageBubble, { message: item, renderContent: renderMessageContent }),
5765
+ ListHeaderComponent: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_react_native45.View, { children: [
5766
+ showTypingIndicator ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react_native45.View, { style: { marginTop: theme.spacing.sm, alignSelf: "flex-start", paddingHorizontal: theme.spacing.lg }, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(TypingIndicator, {}) }) : null,
5767
+ bottomInset > 0 ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react_native45.View, { style: { height: bottomInset } }) : null
5685
5768
  ] })
5686
5769
  }
5687
5770
  );
@@ -5700,6 +5783,7 @@ function ChatPage({
5700
5783
  composer,
5701
5784
  overlay,
5702
5785
  style,
5786
+ composerHorizontalPadding,
5703
5787
  onNearBottomChange,
5704
5788
  listRef
5705
5789
  }) {
@@ -5707,21 +5791,16 @@ function ChatPage({
5707
5791
  const insets = (0, import_react_native_safe_area_context4.useSafeAreaInsets)();
5708
5792
  const [composerHeight, setComposerHeight] = React34.useState(0);
5709
5793
  const [keyboardVisible, setKeyboardVisible] = React34.useState(false);
5710
- const animatedKeyboard = (0, import_react_native_reanimated2.useAnimatedKeyboard)();
5711
5794
  React34.useEffect(() => {
5712
- if (import_react_native45.Platform.OS !== "ios") return;
5713
- const show = import_react_native45.Keyboard.addListener("keyboardWillShow", () => setKeyboardVisible(true));
5714
- const hide = import_react_native45.Keyboard.addListener("keyboardWillHide", () => setKeyboardVisible(false));
5795
+ if (import_react_native46.Platform.OS !== "ios") return;
5796
+ const show = import_react_native46.Keyboard.addListener("keyboardWillShow", () => setKeyboardVisible(true));
5797
+ const hide = import_react_native46.Keyboard.addListener("keyboardWillHide", () => setKeyboardVisible(false));
5715
5798
  return () => {
5716
5799
  show.remove();
5717
5800
  hide.remove();
5718
5801
  };
5719
5802
  }, []);
5720
- const footerBottomPadding = import_react_native45.Platform.OS === "ios" ? keyboardVisible ? 0 : insets.bottom : insets.bottom + 10;
5721
- const footerAnimatedStyle = (0, import_react_native_reanimated2.useAnimatedStyle)(() => {
5722
- if (import_react_native45.Platform.OS !== "ios") return { paddingBottom: insets.bottom + 10 };
5723
- return { paddingBottom: animatedKeyboard.height.value > 0 ? 0 : insets.bottom };
5724
- });
5803
+ const footerBottomPadding = import_react_native46.Platform.OS === "ios" ? keyboardVisible ? 0 : insets.bottom : insets.bottom + 10;
5725
5804
  const overlayBottom = composerHeight + footerBottomPadding + theme.spacing.lg;
5726
5805
  const bottomInset = composerHeight + footerBottomPadding + theme.spacing.xl;
5727
5806
  const resolvedOverlay = React34.useMemo(() => {
@@ -5733,36 +5812,42 @@ function ChatPage({
5733
5812
  style: [prevStyle, { bottom: overlayBottom }]
5734
5813
  });
5735
5814
  }, [overlay, overlayBottom]);
5736
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_react_native45.View, { style: [{ flex: 1 }, style], children: [
5737
- header ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react_native45.View, { children: header }) : null,
5738
- topBanner ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react_native45.View, { style: { paddingHorizontal: theme.spacing.lg, paddingTop: theme.spacing.sm }, children: topBanner }) : null,
5739
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_react_native45.View, { style: { flex: 1 }, children: [
5740
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
5741
- ChatMessageList,
5815
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_react_native46.View, { style: [{ flex: 1 }, style], children: [
5816
+ header ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react_native46.View, { children: header }) : null,
5817
+ topBanner ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react_native46.View, { style: { paddingHorizontal: theme.spacing.lg, paddingTop: theme.spacing.sm }, children: topBanner }) : null,
5818
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_react_native46.View, { style: { flex: 1 }, children: [
5819
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
5820
+ import_react_native46.View,
5742
5821
  {
5743
- ref: listRef,
5744
- messages,
5745
- showTypingIndicator,
5746
- renderMessageContent,
5747
- onNearBottomChange,
5748
- bottomInset
5822
+ style: { flex: 1 },
5823
+ children: [
5824
+ /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
5825
+ ChatMessageList,
5826
+ {
5827
+ ref: listRef,
5828
+ messages,
5829
+ showTypingIndicator,
5830
+ renderMessageContent,
5831
+ onNearBottomChange,
5832
+ bottomInset
5833
+ }
5834
+ ),
5835
+ resolvedOverlay
5836
+ ]
5749
5837
  }
5750
5838
  ),
5751
- resolvedOverlay,
5752
5839
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
5753
- import_react_native_reanimated2.default.View,
5840
+ import_react_native46.View,
5754
5841
  {
5755
- style: [
5756
- {
5757
- position: "absolute",
5758
- left: 0,
5759
- right: 0,
5760
- bottom: 0,
5761
- paddingHorizontal: theme.spacing.lg,
5762
- paddingTop: theme.spacing.sm
5763
- },
5764
- footerAnimatedStyle
5765
- ],
5842
+ style: {
5843
+ position: "absolute",
5844
+ left: 0,
5845
+ right: 0,
5846
+ bottom: 0,
5847
+ paddingHorizontal: composerHorizontalPadding ?? theme.spacing.md,
5848
+ paddingTop: theme.spacing.sm,
5849
+ paddingBottom: footerBottomPadding
5850
+ },
5766
5851
  children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
5767
5852
  ChatComposer,
5768
5853
  {
@@ -5779,24 +5864,24 @@ function ChatPage({
5779
5864
 
5780
5865
  // src/components/chat/ScrollToBottomButton.tsx
5781
5866
  var React35 = __toESM(require("react"));
5782
- var import_react_native46 = require("react-native");
5783
- var import_react_native_reanimated3 = __toESM(require("react-native-reanimated"));
5867
+ var import_react_native47 = require("react-native");
5868
+ var import_react_native_reanimated2 = __toESM(require("react-native-reanimated"));
5784
5869
  var import_jsx_runtime48 = require("react/jsx-runtime");
5785
5870
  function ScrollToBottomButton({ visible, onPress, children, style }) {
5786
5871
  const theme = useTheme();
5787
- const progress = (0, import_react_native_reanimated3.useSharedValue)(visible ? 1 : 0);
5872
+ const progress = (0, import_react_native_reanimated2.useSharedValue)(visible ? 1 : 0);
5788
5873
  const [pressed, setPressed] = React35.useState(false);
5789
5874
  React35.useEffect(() => {
5790
- progress.value = (0, import_react_native_reanimated3.withTiming)(visible ? 1 : 0, { duration: 200, easing: import_react_native_reanimated3.Easing.out(import_react_native_reanimated3.Easing.ease) });
5875
+ 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) });
5791
5876
  }, [progress, visible]);
5792
- const animStyle = (0, import_react_native_reanimated3.useAnimatedStyle)(() => ({
5877
+ const animStyle = (0, import_react_native_reanimated2.useAnimatedStyle)(() => ({
5793
5878
  opacity: progress.value,
5794
5879
  transform: [{ translateY: (1 - progress.value) * 20 }]
5795
5880
  }));
5796
5881
  const bg = theme.scheme === "dark" ? "rgba(39,39,42,0.9)" : "rgba(244,244,245,0.95)";
5797
5882
  const border = theme.scheme === "dark" ? withAlpha("#FFFFFF", 0.12) : withAlpha("#000000", 0.08);
5798
5883
  return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
5799
- import_react_native_reanimated3.default.View,
5884
+ import_react_native_reanimated2.default.View,
5800
5885
  {
5801
5886
  pointerEvents: visible ? "auto" : "none",
5802
5887
  style: [
@@ -5810,7 +5895,7 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
5810
5895
  animStyle
5811
5896
  ],
5812
5897
  children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
5813
- import_react_native46.View,
5898
+ import_react_native47.View,
5814
5899
  {
5815
5900
  style: {
5816
5901
  width: 44,
@@ -5829,7 +5914,7 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
5829
5914
  opacity: pressed ? 0.85 : 1
5830
5915
  },
5831
5916
  children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
5832
- import_react_native46.Pressable,
5917
+ import_react_native47.Pressable,
5833
5918
  {
5834
5919
  onPress,
5835
5920
  onPressIn: () => setPressed(true),
@@ -5846,10 +5931,10 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
5846
5931
  }
5847
5932
 
5848
5933
  // src/components/chat/ChatHeader.tsx
5849
- var import_react_native47 = require("react-native");
5934
+ var import_react_native48 = require("react-native");
5850
5935
  var import_jsx_runtime49 = require("react/jsx-runtime");
5851
5936
  function ChatHeader({ left, right, center, style }) {
5852
- const flattenedStyle = import_react_native47.StyleSheet.flatten([
5937
+ const flattenedStyle = import_react_native48.StyleSheet.flatten([
5853
5938
  {
5854
5939
  paddingTop: 0
5855
5940
  },
@@ -5867,7 +5952,7 @@ function ChatHeader({ left, right, center, style }) {
5867
5952
  }
5868
5953
 
5869
5954
  // src/components/chat/ForkNoticeBanner.tsx
5870
- var import_react_native48 = require("react-native");
5955
+ var import_react_native49 = require("react-native");
5871
5956
  var import_jsx_runtime50 = require("react/jsx-runtime");
5872
5957
  function ForkNoticeBanner({ isOwner = true, title, description, style }) {
5873
5958
  const theme = useTheme();
@@ -5888,7 +5973,7 @@ function ForkNoticeBanner({ isOwner = true, title, description, style }) {
5888
5973
  },
5889
5974
  style
5890
5975
  ],
5891
- children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_react_native48.View, { style: { minWidth: 0 }, children: [
5976
+ children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_react_native49.View, { style: { minWidth: 0 }, children: [
5892
5977
  /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
5893
5978
  Text,
5894
5979
  {
@@ -5964,11 +6049,11 @@ function ChatPanel({
5964
6049
  const header = /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
5965
6050
  ChatHeader,
5966
6051
  {
5967
- left: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_react_native49.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
6052
+ left: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_react_native50.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
5968
6053
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(StudioSheetHeaderIconButton, { onPress: onBack, accessibilityLabel: "Back", style: { marginRight: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(IconBack, { size: 20, colorToken: "floatingContent" }) }),
5969
6054
  onNavigateHome ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(StudioSheetHeaderIconButton, { onPress: onNavigateHome, accessibilityLabel: "Home", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(IconHome, { size: 20, colorToken: "floatingContent" }) }) : null
5970
6055
  ] }),
5971
- right: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_react_native49.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
6056
+ right: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_react_native50.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
5972
6057
  onStartDraw ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(StudioSheetHeaderIconButton, { onPress: onStartDraw, accessibilityLabel: "Draw", intent: "danger", style: { marginRight: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(IconDraw, { size: 20, colorToken: "onDanger" }) }) : null,
5973
6058
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(IconClose, { size: 20, colorToken: "floatingContent" }) })
5974
6059
  ] }),
@@ -5984,12 +6069,12 @@ function ChatPanel({
5984
6069
  ) : null;
5985
6070
  const showMessagesLoading = Boolean(loading) && messages.length === 0 || forking;
5986
6071
  if (showMessagesLoading) {
5987
- return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_react_native49.View, { style: { flex: 1 }, children: [
5988
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native49.View, { children: header }),
5989
- topBanner ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native49.View, { style: { paddingHorizontal: 16, paddingTop: 8 }, children: topBanner }) : null,
5990
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_react_native49.View, { style: { flex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 24, paddingVertical: 12 }, children: [
5991
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native49.ActivityIndicator, {}),
5992
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native49.View, { style: { height: 12 } }),
6072
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_react_native50.View, { style: { flex: 1 }, children: [
6073
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native50.View, { children: header }),
6074
+ topBanner ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native50.View, { style: { paddingHorizontal: 16, paddingTop: 8 }, children: topBanner }) : null,
6075
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_react_native50.View, { style: { flex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 24, paddingVertical: 12 }, children: [
6076
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native50.ActivityIndicator, {}),
6077
+ /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native50.View, { style: { height: 12 } }),
5993
6078
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(Text, { variant: "bodyMuted", children: forking ? "Creating your copy\u2026" : "Loading messages\u2026" })
5994
6079
  ] })
5995
6080
  ] });
@@ -6001,6 +6086,7 @@ function ChatPanel({
6001
6086
  messages,
6002
6087
  showTypingIndicator,
6003
6088
  topBanner,
6089
+ composerHorizontalPadding: 0,
6004
6090
  listRef,
6005
6091
  onNearBottomChange: setNearBottom,
6006
6092
  overlay: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
@@ -6031,10 +6117,10 @@ function ChatPanel({
6031
6117
 
6032
6118
  // src/components/dialogs/ConfirmMergeRequestDialog.tsx
6033
6119
  var React37 = __toESM(require("react"));
6034
- var import_react_native51 = require("react-native");
6120
+ var import_react_native52 = require("react-native");
6035
6121
 
6036
6122
  // src/components/primitives/Modal.tsx
6037
- var import_react_native50 = require("react-native");
6123
+ var import_react_native51 = require("react-native");
6038
6124
  var import_jsx_runtime52 = require("react/jsx-runtime");
6039
6125
  function Modal({
6040
6126
  visible,
@@ -6045,15 +6131,15 @@ function Modal({
6045
6131
  }) {
6046
6132
  const theme = useTheme();
6047
6133
  return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
6048
- import_react_native50.Modal,
6134
+ import_react_native51.Modal,
6049
6135
  {
6050
6136
  visible,
6051
6137
  transparent: true,
6052
6138
  animationType: "fade",
6053
6139
  onRequestClose,
6054
- children: /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_react_native50.View, { style: { flex: 1, backgroundColor: theme.colors.backdrop, justifyContent: "center", padding: theme.spacing.lg }, children: [
6140
+ children: /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_react_native51.View, { style: { flex: 1, backgroundColor: theme.colors.backdrop, justifyContent: "center", padding: theme.spacing.lg }, children: [
6055
6141
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
6056
- import_react_native50.Pressable,
6142
+ import_react_native51.Pressable,
6057
6143
  {
6058
6144
  accessibilityRole: "button",
6059
6145
  onPress: dismissOnBackdropPress ? onRequestClose : void 0,
@@ -6108,7 +6194,7 @@ function ConfirmMergeRequestDialog({
6108
6194
  backgroundColor: theme.colors.background
6109
6195
  },
6110
6196
  children: [
6111
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native51.View, { children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6197
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native52.View, { children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6112
6198
  Text,
6113
6199
  {
6114
6200
  style: {
@@ -6120,9 +6206,9 @@ function ConfirmMergeRequestDialog({
6120
6206
  children: "Are you sure you want to approve this merge request?"
6121
6207
  }
6122
6208
  ) }),
6123
- /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_react_native51.View, { style: { marginTop: 16 }, children: [
6209
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_react_native52.View, { style: { marginTop: 16 }, children: [
6124
6210
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6125
- import_react_native51.View,
6211
+ import_react_native52.View,
6126
6212
  {
6127
6213
  style: [
6128
6214
  fullWidthButtonBase,
@@ -6132,7 +6218,7 @@ function ConfirmMergeRequestDialog({
6132
6218
  }
6133
6219
  ],
6134
6220
  children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6135
- import_react_native51.Pressable,
6221
+ import_react_native52.Pressable,
6136
6222
  {
6137
6223
  accessibilityRole: "button",
6138
6224
  accessibilityLabel: "Approve Merge",
@@ -6144,9 +6230,9 @@ function ConfirmMergeRequestDialog({
6144
6230
  )
6145
6231
  }
6146
6232
  ),
6147
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native51.View, { style: { height: 8 } }),
6233
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native52.View, { style: { height: 8 } }),
6148
6234
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6149
- import_react_native51.View,
6235
+ import_react_native52.View,
6150
6236
  {
6151
6237
  style: [
6152
6238
  fullWidthButtonBase,
@@ -6158,7 +6244,7 @@ function ConfirmMergeRequestDialog({
6158
6244
  }
6159
6245
  ],
6160
6246
  children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6161
- import_react_native51.Pressable,
6247
+ import_react_native52.Pressable,
6162
6248
  {
6163
6249
  accessibilityRole: "button",
6164
6250
  accessibilityLabel: isBuilding ? "Preparing\u2026" : "Test edits first",
@@ -6170,9 +6256,9 @@ function ConfirmMergeRequestDialog({
6170
6256
  )
6171
6257
  }
6172
6258
  ),
6173
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native51.View, { style: { height: 8 } }),
6259
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native52.View, { style: { height: 8 } }),
6174
6260
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6175
- import_react_native51.View,
6261
+ import_react_native52.View,
6176
6262
  {
6177
6263
  style: [
6178
6264
  fullWidthButtonBase,
@@ -6183,7 +6269,7 @@ function ConfirmMergeRequestDialog({
6183
6269
  }
6184
6270
  ],
6185
6271
  children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6186
- import_react_native51.Pressable,
6272
+ import_react_native52.Pressable,
6187
6273
  {
6188
6274
  accessibilityRole: "button",
6189
6275
  accessibilityLabel: "Cancel",
@@ -6233,6 +6319,92 @@ function ConfirmMergeFlow({
6233
6319
  );
6234
6320
  }
6235
6321
 
6322
+ // src/studio/hooks/useOptimisticChatMessages.ts
6323
+ var React38 = __toESM(require("react"));
6324
+ function makeOptimisticId() {
6325
+ return `optimistic:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 10)}`;
6326
+ }
6327
+ function toEpochMs(createdAt) {
6328
+ if (createdAt == null) return 0;
6329
+ if (typeof createdAt === "number") return createdAt;
6330
+ if (createdAt instanceof Date) return createdAt.getTime();
6331
+ const t = Date.parse(String(createdAt));
6332
+ return Number.isFinite(t) ? t : 0;
6333
+ }
6334
+ function isOptimisticResolvedByServer(chatMessages, o) {
6335
+ if (o.failed) return false;
6336
+ const normalize = (s) => s.trim();
6337
+ let startIndex = -1;
6338
+ if (o.baseServerLastId) {
6339
+ startIndex = chatMessages.findIndex((m) => m.id === o.baseServerLastId);
6340
+ }
6341
+ const candidates = startIndex >= 0 ? chatMessages.slice(startIndex + 1) : chatMessages;
6342
+ const target = normalize(o.content);
6343
+ for (const m of candidates) {
6344
+ if (m.author !== "human") continue;
6345
+ if (normalize(m.content) !== target) continue;
6346
+ const serverMs = toEpochMs(m.createdAt);
6347
+ const optimisticMs = Date.parse(o.createdAtIso);
6348
+ if (Number.isFinite(optimisticMs) && optimisticMs > 0 && serverMs > 0) {
6349
+ if (serverMs + 12e4 < optimisticMs) continue;
6350
+ }
6351
+ return true;
6352
+ }
6353
+ return false;
6354
+ }
6355
+ function useOptimisticChatMessages({
6356
+ threadId,
6357
+ shouldForkOnEdit,
6358
+ chatMessages,
6359
+ onSendChat
6360
+ }) {
6361
+ const [optimisticChat, setOptimisticChat] = React38.useState([]);
6362
+ React38.useEffect(() => {
6363
+ setOptimisticChat([]);
6364
+ }, [threadId]);
6365
+ const messages = React38.useMemo(() => {
6366
+ if (!optimisticChat || optimisticChat.length === 0) return chatMessages;
6367
+ const unresolved = optimisticChat.filter((o) => !isOptimisticResolvedByServer(chatMessages, o));
6368
+ if (unresolved.length === 0) return chatMessages;
6369
+ const optimisticAsChat = unresolved.map((o) => ({
6370
+ id: o.id,
6371
+ author: "human",
6372
+ content: o.content,
6373
+ createdAt: o.createdAtIso,
6374
+ kind: "optimistic",
6375
+ meta: o.failed ? { kind: "optimistic", event: "send.failed", status: "error" } : { kind: "optimistic", event: "send.pending", status: "info" }
6376
+ }));
6377
+ const merged = [...chatMessages, ...optimisticAsChat];
6378
+ merged.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
6379
+ return merged;
6380
+ }, [chatMessages, optimisticChat]);
6381
+ React38.useEffect(() => {
6382
+ if (optimisticChat.length === 0) return;
6383
+ setOptimisticChat((prev) => {
6384
+ if (prev.length === 0) return prev;
6385
+ const next = prev.filter((o) => !isOptimisticResolvedByServer(chatMessages, o) || o.failed);
6386
+ return next.length === prev.length ? prev : next;
6387
+ });
6388
+ }, [chatMessages, optimisticChat.length]);
6389
+ const onSend = React38.useCallback(
6390
+ async (text, attachments) => {
6391
+ if (shouldForkOnEdit) {
6392
+ await onSendChat(text, attachments);
6393
+ return;
6394
+ }
6395
+ const createdAtIso = (/* @__PURE__ */ new Date()).toISOString();
6396
+ const baseServerLastId = chatMessages.length > 0 ? chatMessages[chatMessages.length - 1].id : null;
6397
+ const id = makeOptimisticId();
6398
+ setOptimisticChat((prev) => [...prev, { id, content: text, createdAtIso, baseServerLastId, failed: false }]);
6399
+ void Promise.resolve(onSendChat(text, attachments)).catch(() => {
6400
+ setOptimisticChat((prev) => prev.map((m) => m.id === id ? { ...m, failed: true } : m));
6401
+ });
6402
+ },
6403
+ [chatMessages, onSendChat, shouldForkOnEdit]
6404
+ );
6405
+ return { messages, onSend };
6406
+ }
6407
+
6236
6408
  // src/studio/ui/StudioOverlay.tsx
6237
6409
  var import_jsx_runtime55 = require("react/jsx-runtime");
6238
6410
  function StudioOverlay({
@@ -6264,30 +6436,40 @@ function StudioOverlay({
6264
6436
  onNavigateHome
6265
6437
  }) {
6266
6438
  const theme = useTheme();
6267
- const { width } = (0, import_react_native52.useWindowDimensions)();
6268
- const [sheetOpen, setSheetOpen] = React38.useState(false);
6269
- const [activePage, setActivePage] = React38.useState("preview");
6270
- const [drawing, setDrawing] = React38.useState(false);
6271
- const [chatAttachments, setChatAttachments] = React38.useState([]);
6272
- const [commentsAppId, setCommentsAppId] = React38.useState(null);
6273
- const [commentsCount, setCommentsCount] = React38.useState(null);
6274
- const [confirmMrId, setConfirmMrId] = React38.useState(null);
6275
- const confirmMr = React38.useMemo(
6439
+ const { width } = (0, import_react_native53.useWindowDimensions)();
6440
+ const [sheetOpen, setSheetOpen] = React39.useState(false);
6441
+ const [activePage, setActivePage] = React39.useState("preview");
6442
+ const [drawing, setDrawing] = React39.useState(false);
6443
+ const [chatAttachments, setChatAttachments] = React39.useState([]);
6444
+ const [commentsAppId, setCommentsAppId] = React39.useState(null);
6445
+ const [commentsCount, setCommentsCount] = React39.useState(null);
6446
+ const threadId = (app == null ? void 0 : app.threadId) ?? null;
6447
+ const optimistic = useOptimisticChatMessages({
6448
+ threadId,
6449
+ shouldForkOnEdit,
6450
+ chatMessages,
6451
+ onSendChat
6452
+ });
6453
+ const [confirmMrId, setConfirmMrId] = React39.useState(null);
6454
+ const confirmMr = React39.useMemo(
6276
6455
  () => confirmMrId ? incomingMergeRequests.find((m) => m.id === confirmMrId) ?? null : null,
6277
6456
  [confirmMrId, incomingMergeRequests]
6278
6457
  );
6279
- const closeSheet = React38.useCallback(() => {
6280
- setSheetOpen(false);
6281
- import_react_native52.Keyboard.dismiss();
6458
+ const handleSheetOpenChange = React39.useCallback((open) => {
6459
+ setSheetOpen(open);
6460
+ if (!open) import_react_native53.Keyboard.dismiss();
6282
6461
  }, []);
6283
- const openSheet = React38.useCallback(() => setSheetOpen(true), []);
6284
- const goToChat = React38.useCallback(() => {
6462
+ const closeSheet = React39.useCallback(() => {
6463
+ handleSheetOpenChange(false);
6464
+ }, [handleSheetOpenChange]);
6465
+ const openSheet = React39.useCallback(() => setSheetOpen(true), []);
6466
+ const goToChat = React39.useCallback(() => {
6285
6467
  setActivePage("chat");
6286
6468
  openSheet();
6287
6469
  }, [openSheet]);
6288
- const backToPreview = React38.useCallback(() => {
6289
- if (import_react_native52.Platform.OS !== "ios") {
6290
- import_react_native52.Keyboard.dismiss();
6470
+ const backToPreview = React39.useCallback(() => {
6471
+ if (import_react_native53.Platform.OS !== "ios") {
6472
+ import_react_native53.Keyboard.dismiss();
6291
6473
  setActivePage("preview");
6292
6474
  return;
6293
6475
  }
@@ -6299,15 +6481,15 @@ function StudioOverlay({
6299
6481
  clearTimeout(t);
6300
6482
  setActivePage("preview");
6301
6483
  };
6302
- const sub = import_react_native52.Keyboard.addListener("keyboardDidHide", finalize);
6484
+ const sub = import_react_native53.Keyboard.addListener("keyboardDidHide", finalize);
6303
6485
  const t = setTimeout(finalize, 350);
6304
- import_react_native52.Keyboard.dismiss();
6486
+ import_react_native53.Keyboard.dismiss();
6305
6487
  }, []);
6306
- const startDraw = React38.useCallback(() => {
6488
+ const startDraw = React39.useCallback(() => {
6307
6489
  setDrawing(true);
6308
6490
  closeSheet();
6309
6491
  }, [closeSheet]);
6310
- const handleDrawCapture = React38.useCallback(
6492
+ const handleDrawCapture = React39.useCallback(
6311
6493
  (dataUrl) => {
6312
6494
  setChatAttachments((prev) => [...prev, dataUrl]);
6313
6495
  setDrawing(false);
@@ -6316,7 +6498,7 @@ function StudioOverlay({
6316
6498
  },
6317
6499
  [openSheet]
6318
6500
  );
6319
- const toggleSheet = React38.useCallback(async () => {
6501
+ const toggleSheet = React39.useCallback(async () => {
6320
6502
  if (!sheetOpen) {
6321
6503
  const shouldExitTest = Boolean(testingMrId) || isTesting;
6322
6504
  if (shouldExitTest) {
@@ -6328,7 +6510,7 @@ function StudioOverlay({
6328
6510
  closeSheet();
6329
6511
  }
6330
6512
  }, [closeSheet, isTesting, onRestoreBase, sheetOpen, testingMrId]);
6331
- const handleTestMr = React38.useCallback(
6513
+ const handleTestMr = React39.useCallback(
6332
6514
  async (mr) => {
6333
6515
  if (!onTestMr) return;
6334
6516
  await onTestMr(mr);
@@ -6338,7 +6520,7 @@ function StudioOverlay({
6338
6520
  );
6339
6521
  return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_jsx_runtime55.Fragment, { children: [
6340
6522
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(EdgeGlowFrame, { visible: isTesting, role: "accent", thickness: 40, intensity: 1 }),
6341
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(StudioBottomSheet, { open: sheetOpen, onOpenChange: setSheetOpen, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6523
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(StudioBottomSheet, { open: sheetOpen, onOpenChange: handleSheetOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6342
6524
  StudioSheetPager,
6343
6525
  {
6344
6526
  activePage,
@@ -6372,7 +6554,7 @@ function StudioOverlay({
6372
6554
  chat: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6373
6555
  ChatPanel,
6374
6556
  {
6375
- messages: chatMessages,
6557
+ messages: optimistic.messages,
6376
6558
  showTypingIndicator: chatShowTypingIndicator,
6377
6559
  loading: chatLoading,
6378
6560
  sendDisabled: chatSendDisabled,
@@ -6387,7 +6569,7 @@ function StudioOverlay({
6387
6569
  onClose: closeSheet,
6388
6570
  onNavigateHome,
6389
6571
  onStartDraw: startDraw,
6390
- onSend: onSendChat
6572
+ onSend: optimistic.onSend
6391
6573
  }
6392
6574
  )
6393
6575
  }
@@ -6400,7 +6582,7 @@ function StudioOverlay({
6400
6582
  badgeCount: incomingMergeRequests.length,
6401
6583
  onPress: toggleSheet,
6402
6584
  isLoading: (app == null ? void 0 : app.status) === "editing",
6403
- children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_react_native52.View, { style: { width: 28, height: 28, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(MergeIcon, { width: 24, height: 24, color: theme.colors.floatingContent }) })
6585
+ children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_react_native53.View, { style: { width: 28, height: 28, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(MergeIcon, { width: 24, height: 24, color: theme.colors.floatingContent }) })
6404
6586
  }
6405
6587
  ),
6406
6588
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
@@ -6446,16 +6628,16 @@ function ComergeStudio({
6446
6628
  onNavigateHome,
6447
6629
  style
6448
6630
  }) {
6449
- const [activeAppId, setActiveAppId] = React39.useState(appId);
6450
- const [runtimeAppId, setRuntimeAppId] = React39.useState(appId);
6451
- const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React39.useState(null);
6452
- const platform = React39.useMemo(() => import_react_native53.Platform.OS === "ios" ? "ios" : "android", []);
6453
- React39.useEffect(() => {
6631
+ const [activeAppId, setActiveAppId] = React40.useState(appId);
6632
+ const [runtimeAppId, setRuntimeAppId] = React40.useState(appId);
6633
+ const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React40.useState(null);
6634
+ const platform = React40.useMemo(() => import_react_native54.Platform.OS === "ios" ? "ios" : "android", []);
6635
+ React40.useEffect(() => {
6454
6636
  setActiveAppId(appId);
6455
6637
  setRuntimeAppId(appId);
6456
6638
  setPendingRuntimeTargetAppId(null);
6457
6639
  }, [appId]);
6458
- const captureTargetRef = React39.useRef(null);
6640
+ const captureTargetRef = React40.useRef(null);
6459
6641
  return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(StudioBootstrap, { apiKey, children: ({ userId }) => /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_bottom_sheet6.BottomSheetModalProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
6460
6642
  ComergeStudioInner,
6461
6643
  {
@@ -6491,11 +6673,11 @@ function ComergeStudioInner({
6491
6673
  const { app, loading: appLoading } = useApp(activeAppId);
6492
6674
  const { app: runtimeAppFromHook } = useApp(runtimeAppId, { enabled: runtimeAppId !== activeAppId });
6493
6675
  const runtimeApp = runtimeAppId === activeAppId ? app : runtimeAppFromHook;
6494
- const sawEditingOnPendingTargetRef = React39.useRef(false);
6495
- React39.useEffect(() => {
6676
+ const sawEditingOnPendingTargetRef = React40.useRef(false);
6677
+ React40.useEffect(() => {
6496
6678
  sawEditingOnPendingTargetRef.current = false;
6497
6679
  }, [pendingRuntimeTargetAppId]);
6498
- React39.useEffect(() => {
6680
+ React40.useEffect(() => {
6499
6681
  if (!pendingRuntimeTargetAppId) return;
6500
6682
  if (activeAppId !== pendingRuntimeTargetAppId) return;
6501
6683
  if ((app == null ? void 0 : app.status) === "editing") {
@@ -6512,13 +6694,38 @@ function ComergeStudioInner({
6512
6694
  platform,
6513
6695
  canRequestLatest: (runtimeApp == null ? void 0 : runtimeApp.status) === "ready"
6514
6696
  });
6697
+ const sawEditingOnActiveAppRef = React40.useRef(false);
6698
+ const [showPostEditPreparing, setShowPostEditPreparing] = React40.useState(false);
6699
+ React40.useEffect(() => {
6700
+ sawEditingOnActiveAppRef.current = false;
6701
+ setShowPostEditPreparing(false);
6702
+ }, [activeAppId]);
6703
+ React40.useEffect(() => {
6704
+ if (!(app == null ? void 0 : app.id)) return;
6705
+ if (app.status === "editing") {
6706
+ sawEditingOnActiveAppRef.current = true;
6707
+ setShowPostEditPreparing(false);
6708
+ return;
6709
+ }
6710
+ if (app.status === "ready" && sawEditingOnActiveAppRef.current) {
6711
+ setShowPostEditPreparing(true);
6712
+ sawEditingOnActiveAppRef.current = false;
6713
+ }
6714
+ }, [app == null ? void 0 : app.id, app == null ? void 0 : app.status]);
6715
+ React40.useEffect(() => {
6716
+ if (!showPostEditPreparing) return;
6717
+ const stillProcessingBaseBundle = bundle.loading && bundle.loadingMode === "base" && !bundle.isTesting;
6718
+ if (!stillProcessingBaseBundle) {
6719
+ setShowPostEditPreparing(false);
6720
+ }
6721
+ }, [showPostEditPreparing, bundle.loading, bundle.loadingMode, bundle.isTesting]);
6515
6722
  const threadId = (app == null ? void 0 : app.threadId) ?? "";
6516
6723
  const thread = useThreadMessages(threadId);
6517
6724
  const mergeRequests = useMergeRequests({ appId: activeAppId });
6518
- const hasOpenOutgoingMr = React39.useMemo(() => {
6725
+ const hasOpenOutgoingMr = React40.useMemo(() => {
6519
6726
  return mergeRequests.lists.outgoing.some((mr) => mr.status === "open");
6520
6727
  }, [mergeRequests.lists.outgoing]);
6521
- const incomingReviewMrs = React39.useMemo(() => {
6728
+ const incomingReviewMrs = React40.useMemo(() => {
6522
6729
  if (!userId) return mergeRequests.lists.incoming;
6523
6730
  return mergeRequests.lists.incoming.filter((mr) => mr.createdBy !== userId);
6524
6731
  }, [mergeRequests.lists.incoming, userId]);
@@ -6540,17 +6747,25 @@ function ComergeStudioInner({
6540
6747
  uploadAttachments: uploader.uploadBase64Images
6541
6748
  });
6542
6749
  const chatSendDisabled = hasNoOutcomeAfterLastHuman(thread.raw);
6543
- const [processingMrId, setProcessingMrId] = React39.useState(null);
6544
- const [testingMrId, setTestingMrId] = React39.useState(null);
6545
- const chatShowTypingIndicator = React39.useMemo(() => {
6750
+ const [processingMrId, setProcessingMrId] = React40.useState(null);
6751
+ const [testingMrId, setTestingMrId] = React40.useState(null);
6752
+ const chatShowTypingIndicator = React40.useMemo(() => {
6546
6753
  var _a;
6547
6754
  if (!thread.raw || thread.raw.length === 0) return false;
6548
6755
  const last = thread.raw[thread.raw.length - 1];
6549
6756
  const payloadType = typeof ((_a = last.payload) == null ? void 0 : _a.type) === "string" ? String(last.payload.type) : void 0;
6550
6757
  return payloadType !== "outcome";
6551
6758
  }, [thread.raw]);
6552
- return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native53.View, { style: [{ flex: 1 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native53.View, { ref: captureTargetRef, style: { flex: 1 }, collapsable: false, children: [
6553
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(RuntimeRenderer, { appKey, bundlePath: bundle.bundlePath, renderToken: bundle.renderToken }),
6759
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(import_react_native54.View, { style: [{ flex: 1 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native54.View, { ref: captureTargetRef, style: { flex: 1 }, collapsable: false, children: [
6760
+ /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
6761
+ RuntimeRenderer,
6762
+ {
6763
+ appKey,
6764
+ bundlePath: bundle.bundlePath,
6765
+ forcePreparing: showPostEditPreparing,
6766
+ renderToken: bundle.renderToken
6767
+ }
6768
+ ),
6554
6769
  /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
6555
6770
  StudioOverlay,
6556
6771
  {