@comergehq/studio 0.1.9 → 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,7 +1901,7 @@ 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
  }
@@ -1829,9 +1930,9 @@ function StudioBottomSheet({
1829
1930
  ref: resolvedSheetRef,
1830
1931
  index: open ? snapPoints.length - 1 : -1,
1831
1932
  snapPoints,
1933
+ enableDynamicSizing: false,
1832
1934
  enablePanDownToClose: true,
1833
- keyboardBehavior: "interactive",
1834
- keyboardBlurBehavior: "restore",
1935
+ enableContentPanningGesture: false,
1835
1936
  android_keyboardInputMode: "adjustResize",
1836
1937
  backgroundComponent: (props) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StudioSheetBackground, { ...props, renderBackground: background == null ? void 0 : background.renderBackground }),
1837
1938
  topInset: insets.top,
@@ -1839,19 +1940,19 @@ function StudioBottomSheet({
1839
1940
  handleIndicatorStyle: { backgroundColor: theme.colors.handleIndicator },
1840
1941
  onChange: handleChange,
1841
1942
  ...bottomSheetProps,
1842
- 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 })
1843
1944
  }
1844
1945
  );
1845
1946
  }
1846
1947
 
1847
1948
  // src/components/studio-sheet/StudioSheetPager.tsx
1848
1949
  var React10 = __toESM(require("react"));
1849
- var import_react_native8 = require("react-native");
1950
+ var import_react_native9 = require("react-native");
1850
1951
  var import_jsx_runtime6 = require("react/jsx-runtime");
1851
1952
  function StudioSheetPager({ activePage, width, preview, chat, style }) {
1852
- 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;
1853
1954
  React10.useEffect(() => {
1854
- import_react_native8.Animated.spring(anim, {
1955
+ import_react_native9.Animated.spring(anim, {
1855
1956
  toValue: activePage === "chat" ? 1 : 0,
1856
1957
  useNativeDriver: true,
1857
1958
  tension: 65,
@@ -1860,9 +1961,9 @@ function StudioSheetPager({ activePage, width, preview, chat, style }) {
1860
1961
  }, [activePage, anim]);
1861
1962
  const previewTranslateX = anim.interpolate({ inputRange: [0, 1], outputRange: [0, -width] });
1862
1963
  const chatTranslateX = anim.interpolate({ inputRange: [0, 1], outputRange: [width, 0] });
1863
- 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: [
1864
1965
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1865
- import_react_native8.Animated.View,
1966
+ import_react_native9.Animated.View,
1866
1967
  {
1867
1968
  style: [
1868
1969
  {
@@ -1879,7 +1980,7 @@ function StudioSheetPager({ activePage, width, preview, chat, style }) {
1879
1980
  }
1880
1981
  ),
1881
1982
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1882
- import_react_native8.Animated.View,
1983
+ import_react_native9.Animated.View,
1883
1984
  {
1884
1985
  style: [
1885
1986
  {
@@ -1900,7 +2001,7 @@ function StudioSheetPager({ activePage, width, preview, chat, style }) {
1900
2001
 
1901
2002
  // src/components/floating-draggable-button/FloatingDraggableButton.tsx
1902
2003
  var import_react = require("react");
1903
- var import_react_native9 = require("react-native");
2004
+ var import_react_native10 = require("react-native");
1904
2005
  var Haptics = __toESM(require("expo-haptics"));
1905
2006
  var import_react_native_reanimated = __toESM(require("react-native-reanimated"));
1906
2007
  var import_liquid_glass2 = require("@callstack/liquid-glass");
@@ -1959,7 +2060,7 @@ function FloatingDraggableButton({
1959
2060
  backgroundColor
1960
2061
  }) {
1961
2062
  const theme = useTheme();
1962
- const { width, height } = (0, import_react_native9.useWindowDimensions)();
2063
+ const { width, height } = (0, import_react_native10.useWindowDimensions)();
1963
2064
  const isDanger = variant === "danger";
1964
2065
  const onPressRef = (0, import_react.useRef)(onPress);
1965
2066
  (0, import_react.useEffect)(() => {
@@ -2062,7 +2163,7 @@ function FloatingDraggableButton({
2062
2163
  }
2063
2164
  }, [forceShowTrigger, visible, animateIn]);
2064
2165
  const panResponder = (0, import_react.useRef)(
2065
- import_react_native9.PanResponder.create({
2166
+ import_react_native10.PanResponder.create({
2066
2167
  onStartShouldSetPanResponder: () => true,
2067
2168
  onMoveShouldSetPanResponder: () => true,
2068
2169
  onPanResponderGrant: () => {
@@ -2125,24 +2226,24 @@ function FloatingDraggableButton({
2125
2226
  interactive: true,
2126
2227
  effect: "clear",
2127
2228
  children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2128
- import_react_native9.Pressable,
2229
+ import_react_native10.Pressable,
2129
2230
  {
2130
2231
  onPress: () => {
2131
2232
  if (!disabled) animateOut();
2132
2233
  },
2133
2234
  style: styles.buttonInner,
2134
2235
  android_ripple: { color: "rgba(255, 255, 255, 0.3)", borderless: true },
2135
- 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, {})
2136
2237
  }
2137
2238
  )
2138
2239
  }
2139
2240
  ) }),
2140
- 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 }) })
2141
2242
  ]
2142
2243
  }
2143
2244
  );
2144
2245
  }
2145
- var styles = import_react_native9.StyleSheet.create({
2246
+ var styles = import_react_native10.StyleSheet.create({
2146
2247
  floatingButton: {
2147
2248
  position: "absolute",
2148
2249
  justifyContent: "center",
@@ -2179,7 +2280,7 @@ var styles = import_react_native9.StyleSheet.create({
2179
2280
 
2180
2281
  // src/components/overlays/EdgeGlowFrame.tsx
2181
2282
  var React11 = __toESM(require("react"));
2182
- var import_react_native10 = require("react-native");
2283
+ var import_react_native11 = require("react-native");
2183
2284
  var import_expo_linear_gradient = require("expo-linear-gradient");
2184
2285
 
2185
2286
  // src/components/utils/color.ts
@@ -2221,9 +2322,9 @@ function EdgeGlowFrame({
2221
2322
  }) {
2222
2323
  const theme = useTheme();
2223
2324
  const alpha = Math.max(0, Math.min(1, intensity));
2224
- 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;
2225
2326
  React11.useEffect(() => {
2226
- import_react_native10.Animated.timing(anim, {
2327
+ import_react_native11.Animated.timing(anim, {
2227
2328
  toValue: visible ? 1 : 0,
2228
2329
  duration: 300,
2229
2330
  useNativeDriver: true
@@ -2232,8 +2333,8 @@ function EdgeGlowFrame({
2232
2333
  const c = baseColor(role, theme);
2233
2334
  const strong = withAlpha(c, 0.6 * alpha);
2234
2335
  const soft = withAlpha(c, 0.22 * alpha);
2235
- return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native10.Animated.View, { pointerEvents: "none", style: [{ position: "absolute", inset: 0, opacity: anim }, style], children: [
2236
- /* @__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)(
2237
2338
  import_expo_linear_gradient.LinearGradient,
2238
2339
  {
2239
2340
  colors: [strong, soft, "transparent"],
@@ -2242,7 +2343,7 @@ function EdgeGlowFrame({
2242
2343
  style: { width: "100%", height: "100%" }
2243
2344
  }
2244
2345
  ) }),
2245
- /* @__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)(
2246
2347
  import_expo_linear_gradient.LinearGradient,
2247
2348
  {
2248
2349
  colors: ["transparent", soft, strong],
@@ -2251,7 +2352,7 @@ function EdgeGlowFrame({
2251
2352
  style: { width: "100%", height: "100%" }
2252
2353
  }
2253
2354
  ) }),
2254
- /* @__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)(
2255
2356
  import_expo_linear_gradient.LinearGradient,
2256
2357
  {
2257
2358
  colors: [strong, soft, "transparent"],
@@ -2260,7 +2361,7 @@ function EdgeGlowFrame({
2260
2361
  style: { width: "100%", height: "100%" }
2261
2362
  }
2262
2363
  ) }),
2263
- /* @__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)(
2264
2365
  import_expo_linear_gradient.LinearGradient,
2265
2366
  {
2266
2367
  colors: ["transparent", soft, strong],
@@ -2274,12 +2375,12 @@ function EdgeGlowFrame({
2274
2375
 
2275
2376
  // src/components/draw/DrawModeOverlay.tsx
2276
2377
  var React14 = __toESM(require("react"));
2277
- var import_react_native14 = require("react-native");
2378
+ var import_react_native15 = require("react-native");
2278
2379
  var import_react_native_view_shot = require("react-native-view-shot");
2279
2380
 
2280
2381
  // src/components/draw/DrawSurface.tsx
2281
2382
  var React12 = __toESM(require("react"));
2282
- var import_react_native11 = require("react-native");
2383
+ var import_react_native12 = require("react-native");
2283
2384
  var import_react_native_svg = __toESM(require("react-native-svg"));
2284
2385
 
2285
2386
  // src/components/draw/strokes.ts
@@ -2350,7 +2451,7 @@ function DrawSurface({
2350
2451
  triggerRender();
2351
2452
  }, [color, onAddStroke, strokeWidth, triggerRender]);
2352
2453
  const panResponder = React12.useMemo(
2353
- () => import_react_native11.PanResponder.create({
2454
+ () => import_react_native12.PanResponder.create({
2354
2455
  onStartShouldSetPanResponder: () => true,
2355
2456
  onMoveShouldSetPanResponder: () => true,
2356
2457
  onPanResponderGrant: onStart,
@@ -2362,7 +2463,7 @@ function DrawSurface({
2362
2463
  );
2363
2464
  const currentPath = pointsToSmoothPath(currentPointsRef.current);
2364
2465
  void renderTick;
2365
- 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: [
2366
2467
  strokes.map((s, idx) => {
2367
2468
  const d = pointsToSmoothPath(s.points);
2368
2469
  if (!d) return null;
@@ -2392,7 +2493,7 @@ function DrawSurface({
2392
2493
  ) : null
2393
2494
  ] }) });
2394
2495
  }
2395
- var styles2 = import_react_native11.StyleSheet.create({
2496
+ var styles2 = import_react_native12.StyleSheet.create({
2396
2497
  container: {
2397
2498
  zIndex: 5
2398
2499
  }
@@ -2400,7 +2501,7 @@ var styles2 = import_react_native11.StyleSheet.create({
2400
2501
 
2401
2502
  // src/components/draw/DrawToolbar.tsx
2402
2503
  var React13 = __toESM(require("react"));
2403
- var import_react_native13 = require("react-native");
2504
+ var import_react_native14 = require("react-native");
2404
2505
  var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
2405
2506
  var import_lucide_react_native = require("lucide-react-native");
2406
2507
 
@@ -2419,7 +2520,7 @@ async function impact(style) {
2419
2520
  }
2420
2521
 
2421
2522
  // src/components/draw/DrawColorPicker.tsx
2422
- var import_react_native12 = require("react-native");
2523
+ var import_react_native13 = require("react-native");
2423
2524
  var import_jsx_runtime10 = require("react/jsx-runtime");
2424
2525
  function DrawColorPicker({
2425
2526
  colors,
@@ -2454,10 +2555,10 @@ function DrawColorPicker({
2454
2555
  return { ...base, ...selectedStyle, ...whiteStyle };
2455
2556
  };
2456
2557
  if (!expanded) {
2457
- 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] });
2458
2559
  }
2459
- 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)(
2460
- 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,
2461
2562
  {
2462
2563
  onPress: () => {
2463
2564
  onSelect(c);
@@ -2488,14 +2589,14 @@ function DrawToolbar({
2488
2589
  style
2489
2590
  }) {
2490
2591
  const insets = (0, import_react_native_safe_area_context2.useSafeAreaInsets)();
2491
- const { width: screenWidth, height: screenHeight } = (0, import_react_native13.useWindowDimensions)();
2592
+ const { width: screenWidth, height: screenHeight } = (0, import_react_native14.useWindowDimensions)();
2492
2593
  const [expanded, setExpanded] = React13.useState(false);
2493
- 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;
2494
2595
  const start = React13.useRef({ x: 0, y: 0 });
2495
2596
  const currentPos = React13.useRef({ x: 0, y: 0 });
2496
2597
  React13.useEffect(() => {
2497
2598
  if (hidden) return;
2498
- import_react_native13.Animated.spring(pos.y, {
2599
+ import_react_native14.Animated.spring(pos.y, {
2499
2600
  toValue: insets.top + 60,
2500
2601
  useNativeDriver: true,
2501
2602
  damping: 12,
@@ -2522,7 +2623,7 @@ function DrawToolbar({
2522
2623
  [insets.top, screenHeight, screenWidth]
2523
2624
  );
2524
2625
  const panResponder = React13.useMemo(
2525
- () => import_react_native13.PanResponder.create({
2626
+ () => import_react_native14.PanResponder.create({
2526
2627
  onStartShouldSetPanResponder: () => false,
2527
2628
  onMoveShouldSetPanResponder: (_e, g) => Math.abs(g.dx) > 5 || Math.abs(g.dy) > 5,
2528
2629
  onPanResponderGrant: () => {
@@ -2534,7 +2635,7 @@ function DrawToolbar({
2534
2635
  },
2535
2636
  onPanResponderRelease: () => {
2536
2637
  const next = clamp2(currentPos.current.x, currentPos.current.y);
2537
- import_react_native13.Animated.spring(pos, { toValue: next, useNativeDriver: true }).start();
2638
+ import_react_native14.Animated.spring(pos, { toValue: next, useNativeDriver: true }).start();
2538
2639
  }
2539
2640
  }),
2540
2641
  [clamp2, pos]
@@ -2551,7 +2652,7 @@ function DrawToolbar({
2551
2652
  const isDisabled = Boolean(disabled) || Boolean(capturingDisabled);
2552
2653
  const [pressed, setPressed] = React13.useState(false);
2553
2654
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2554
- import_react_native13.View,
2655
+ import_react_native14.View,
2555
2656
  {
2556
2657
  style: {
2557
2658
  width: 28,
@@ -2563,7 +2664,7 @@ function DrawToolbar({
2563
2664
  opacity: isDisabled ? 0.5 : pressed ? 0.85 : 1
2564
2665
  },
2565
2666
  children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2566
- import_react_native13.Pressable,
2667
+ import_react_native14.Pressable,
2567
2668
  {
2568
2669
  accessibilityRole: "button",
2569
2670
  accessibilityLabel,
@@ -2580,7 +2681,7 @@ function DrawToolbar({
2580
2681
  );
2581
2682
  }
2582
2683
  return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2583
- import_react_native13.Animated.View,
2684
+ import_react_native14.Animated.View,
2584
2685
  {
2585
2686
  style: [
2586
2687
  {
@@ -2597,7 +2698,7 @@ function DrawToolbar({
2597
2698
  ],
2598
2699
  ...panResponder.panHandlers,
2599
2700
  children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2600
- import_react_native13.View,
2701
+ import_react_native14.View,
2601
2702
  {
2602
2703
  style: {
2603
2704
  backgroundColor: "#F43F5E",
@@ -2605,7 +2706,7 @@ function DrawToolbar({
2605
2706
  padding: 12,
2606
2707
  minWidth: 220
2607
2708
  },
2608
- 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: [
2609
2710
  renderDragHandle ? renderDragHandle() : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react_native.GripVertical, { size: 20, color: "rgba(255, 255, 255, 0.6)" }),
2610
2711
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2611
2712
  DrawColorPicker,
@@ -2623,7 +2724,7 @@ function DrawToolbar({
2623
2724
  }
2624
2725
  }
2625
2726
  ),
2626
- /* @__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 } }),
2627
2728
  /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2628
2729
  CircleActionButton,
2629
2730
  {
@@ -2661,7 +2762,7 @@ function DrawToolbar({
2661
2762
  void impact("medium");
2662
2763
  onDone();
2663
2764
  },
2664
- 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" })
2665
2766
  }
2666
2767
  )
2667
2768
  ] })
@@ -2746,7 +2847,7 @@ function DrawModeOverlay({
2746
2847
  }
2747
2848
  }, [captureTargetRef, capturing, onCapture]);
2748
2849
  if (!visible) return null;
2749
- 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: [
2750
2851
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(EdgeGlowFrame, { visible: !hideUi, role: "danger", thickness: 50, intensity: 1 }),
2751
2852
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2752
2853
  DrawSurface,
@@ -2777,7 +2878,7 @@ function DrawModeOverlay({
2777
2878
  )
2778
2879
  ] });
2779
2880
  }
2780
- var styles3 = import_react_native14.StyleSheet.create({
2881
+ var styles3 = import_react_native15.StyleSheet.create({
2781
2882
  root: {
2782
2883
  zIndex: 9999
2783
2884
  }
@@ -2785,7 +2886,7 @@ var styles3 = import_react_native14.StyleSheet.create({
2785
2886
 
2786
2887
  // src/components/comments/AppCommentsSheet.tsx
2787
2888
  var React21 = __toESM(require("react"));
2788
- var import_react_native20 = require("react-native");
2889
+ var import_react_native21 = require("react-native");
2789
2890
  var import_bottom_sheet3 = require("@gorhom/bottom-sheet");
2790
2891
  var import_react_native_safe_area_context3 = require("react-native-safe-area-context");
2791
2892
  var import_liquid_glass4 = require("@callstack/liquid-glass");
@@ -2793,13 +2894,13 @@ var import_lucide_react_native4 = require("lucide-react-native");
2793
2894
 
2794
2895
  // src/components/chat/ChatComposer.tsx
2795
2896
  var React16 = __toESM(require("react"));
2796
- var import_react_native16 = require("react-native");
2897
+ var import_react_native17 = require("react-native");
2797
2898
  var import_liquid_glass3 = require("@callstack/liquid-glass");
2798
2899
  var import_lucide_react_native3 = require("lucide-react-native");
2799
2900
 
2800
2901
  // src/components/chat/MultilineTextInput.tsx
2801
2902
  var React15 = __toESM(require("react"));
2802
- var import_react_native15 = require("react-native");
2903
+ var import_react_native16 = require("react-native");
2803
2904
  var import_bottom_sheet2 = require("@gorhom/bottom-sheet");
2804
2905
  var import_jsx_runtime13 = require("react/jsx-runtime");
2805
2906
  var MultilineTextInput = React15.forwardRef(function MultilineTextInput2({ useBottomSheetTextInput = false, placeholder, placeholderTextColor, style, ...props }, ref) {
@@ -2822,7 +2923,7 @@ var MultilineTextInput = React15.forwardRef(function MultilineTextInput2({ useBo
2822
2923
  style: [baseStyle, style],
2823
2924
  textAlignVertical: "top"
2824
2925
  };
2825
- 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 });
2826
2927
  });
2827
2928
 
2828
2929
  // src/components/icons/StudioIcons.tsx
@@ -2875,9 +2976,9 @@ function AspectRatioThumbnail({
2875
2976
  renderRemoveIcon
2876
2977
  }) {
2877
2978
  const [aspectRatio, setAspectRatio] = React16.useState(1);
2878
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native16.View, { style: { height: THUMBNAIL_HEIGHT, aspectRatio, position: "relative" }, children: [
2879
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native16.View, { style: { flex: 1, borderRadius: 8, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2880
- 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,
2881
2982
  {
2882
2983
  source: { uri },
2883
2984
  style: { width: "100%", height: "100%" },
@@ -2890,7 +2991,7 @@ function AspectRatioThumbnail({
2890
2991
  }
2891
2992
  ) }),
2892
2993
  onRemove ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2893
- import_react_native16.Pressable,
2994
+ import_react_native17.Pressable,
2894
2995
  {
2895
2996
  style: {
2896
2997
  position: "absolute",
@@ -2938,8 +3039,8 @@ function ChatComposer({
2938
3039
  const hasText = text.trim().length > 0;
2939
3040
  const composerMinHeight = hasAttachments ? THUMBNAIL_HEIGHT + 44 + 24 : 44;
2940
3041
  const isButtonDisabled = sending || disabled || sendDisabled;
2941
- const maxInputHeight = React16.useMemo(() => import_react_native16.Dimensions.get("window").height * 0.5, []);
2942
- 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;
2943
3044
  const [sendPressed, setSendPressed] = React16.useState(false);
2944
3045
  const inputRef = React16.useRef(null);
2945
3046
  const prevAutoFocusRef = React16.useRef(false);
@@ -2955,12 +3056,12 @@ function ChatComposer({
2955
3056
  }, [autoFocus, disabled, sending]);
2956
3057
  const triggerShake = React16.useCallback(() => {
2957
3058
  shakeAnim.setValue(0);
2958
- import_react_native16.Animated.sequence([
2959
- import_react_native16.Animated.timing(shakeAnim, { toValue: 10, duration: 50, useNativeDriver: true }),
2960
- import_react_native16.Animated.timing(shakeAnim, { toValue: -10, duration: 50, useNativeDriver: true }),
2961
- import_react_native16.Animated.timing(shakeAnim, { toValue: 10, duration: 50, useNativeDriver: true }),
2962
- import_react_native16.Animated.timing(shakeAnim, { toValue: -10, duration: 50, useNativeDriver: true }),
2963
- 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 })
2964
3065
  ]).start();
2965
3066
  }, [shakeAnim]);
2966
3067
  const handleSend = React16.useCallback(async () => {
@@ -2976,12 +3077,12 @@ function ChatComposer({
2976
3077
  const textareaBgColor = theme.scheme === "dark" ? "#18181B" : "#F6F6F6";
2977
3078
  const placeholderTextColor = theme.scheme === "dark" ? "#A1A1AA" : "#71717A";
2978
3079
  return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2979
- import_react_native16.View,
3080
+ import_react_native17.View,
2980
3081
  {
2981
3082
  style: [{ paddingHorizontal: 16, paddingBottom: 12, paddingTop: 8 }, style],
2982
3083
  onLayout: (e) => onLayout == null ? void 0 : onLayout({ height: e.nativeEvent.layout.height }),
2983
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(import_react_native16.View, { style: { flexDirection: "row", alignItems: "flex-end", gap: 8 }, children: [
2984
- /* @__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)(
2985
3086
  import_liquid_glass3.LiquidGlassView,
2986
3087
  {
2987
3088
  style: [
@@ -2994,7 +3095,7 @@ function ChatComposer({
2994
3095
  effect: "clear",
2995
3096
  children: [
2996
3097
  hasAttachments ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2997
- import_react_native16.ScrollView,
3098
+ import_react_native17.ScrollView,
2998
3099
  {
2999
3100
  horizontal: true,
3000
3101
  showsHorizontalScrollIndicator: false,
@@ -3011,7 +3112,7 @@ function ChatComposer({
3011
3112
  `attachment-${index}`
3012
3113
  )),
3013
3114
  onAddAttachment ? renderAddAttachment ? renderAddAttachment() : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3014
- import_react_native16.Pressable,
3115
+ import_react_native17.Pressable,
3015
3116
  {
3016
3117
  style: {
3017
3118
  height: THUMBNAIL_HEIGHT,
@@ -3063,7 +3164,7 @@ function ChatComposer({
3063
3164
  interactive: true,
3064
3165
  effect: "clear",
3065
3166
  children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3066
- import_react_native16.View,
3167
+ import_react_native17.View,
3067
3168
  {
3068
3169
  style: {
3069
3170
  width: 44,
@@ -3074,7 +3175,7 @@ function ChatComposer({
3074
3175
  opacity: isButtonDisabled ? 0.6 : sendPressed ? 0.9 : 1
3075
3176
  },
3076
3177
  children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3077
- import_react_native16.Pressable,
3178
+ import_react_native17.Pressable,
3078
3179
  {
3079
3180
  accessibilityRole: "button",
3080
3181
  accessibilityLabel: "Send",
@@ -3083,7 +3184,7 @@ function ChatComposer({
3083
3184
  onPressIn: () => setSendPressed(true),
3084
3185
  onPressOut: () => setSendPressed(false),
3085
3186
  style: { flex: 1, alignItems: "center", justifyContent: "center" },
3086
- 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" })
3087
3188
  }
3088
3189
  )
3089
3190
  }
@@ -3097,10 +3198,10 @@ function ChatComposer({
3097
3198
 
3098
3199
  // src/components/comments/CommentRow.tsx
3099
3200
  var React17 = __toESM(require("react"));
3100
- var import_react_native18 = require("react-native");
3201
+ var import_react_native19 = require("react-native");
3101
3202
 
3102
3203
  // src/components/primitives/Avatar.tsx
3103
- var import_react_native17 = require("react-native");
3204
+ var import_react_native18 = require("react-native");
3104
3205
  var import_jsx_runtime16 = require("react/jsx-runtime");
3105
3206
  function initialsFrom(name) {
3106
3207
  var _a, _b;
@@ -3120,7 +3221,7 @@ function Avatar({
3120
3221
  const radius = size / 2;
3121
3222
  const fallbackBg = fallbackBackgroundColor ?? theme.colors.neutral;
3122
3223
  return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3123
- import_react_native17.View,
3224
+ import_react_native18.View,
3124
3225
  {
3125
3226
  style: [
3126
3227
  {
@@ -3135,7 +3236,7 @@ function Avatar({
3135
3236
  style
3136
3237
  ],
3137
3238
  children: uri ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3138
- import_react_native17.Image,
3239
+ import_react_native18.Image,
3139
3240
  {
3140
3241
  source: { uri },
3141
3242
  style: [{ width: size, height: size }, imageStyle],
@@ -3186,7 +3287,7 @@ function CommentRow({ comment, showDivider }) {
3186
3287
  };
3187
3288
  }, [comment.authorId]);
3188
3289
  return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3189
- import_react_native18.View,
3290
+ import_react_native19.View,
3190
3291
  {
3191
3292
  style: {
3192
3293
  flexDirection: "row",
@@ -3197,8 +3298,8 @@ function CommentRow({ comment, showDivider }) {
3197
3298
  },
3198
3299
  children: [
3199
3300
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Avatar, { size: 32, uri: authorAvatar, name: authorName ?? comment.authorId, style: { marginTop: 6 } }),
3200
- /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react_native18.View, { style: { flex: 1, minWidth: 0, gap: 4 }, children: [
3201
- /* @__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: [
3202
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" }),
3203
3304
  /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { style: { fontSize: 12, lineHeight: 16, color: theme.colors.textMuted }, children: formatTimeAgo(comment.createdAt) })
3204
3305
  ] }),
@@ -3366,13 +3467,13 @@ function useAppDetails(appId) {
3366
3467
 
3367
3468
  // src/components/comments/useIosKeyboardSnapFix.ts
3368
3469
  var React20 = __toESM(require("react"));
3369
- var import_react_native19 = require("react-native");
3470
+ var import_react_native20 = require("react-native");
3370
3471
  function useIosKeyboardSnapFix(sheetRef, options) {
3371
3472
  const [keyboardVisible, setKeyboardVisible] = React20.useState(false);
3372
3473
  React20.useEffect(() => {
3373
- if (import_react_native19.Platform.OS !== "ios") return;
3374
- const show = import_react_native19.Keyboard.addListener("keyboardWillShow", () => setKeyboardVisible(true));
3375
- 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", () => {
3376
3477
  var _a;
3377
3478
  setKeyboardVisible(false);
3378
3479
  const target = (options == null ? void 0 : options.targetIndex) ?? 1;
@@ -3448,17 +3549,17 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3448
3549
  onChange: handleChange,
3449
3550
  backgroundStyle: {
3450
3551
  backgroundColor: theme.scheme === "dark" ? "#0B080F" : "#FFFFFF",
3451
- borderTopLeftRadius: import_react_native20.Platform.OS === "ios" ? 39 : 16,
3452
- 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
3453
3554
  },
3454
3555
  handleIndicatorStyle: { backgroundColor: theme.colors.handleIndicator },
3455
3556
  keyboardBehavior: "interactive",
3456
3557
  keyboardBlurBehavior: "restore",
3457
3558
  android_keyboardInputMode: "adjustResize",
3458
3559
  topInset: insets.top,
3459
- 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: [
3460
3561
  /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
3461
- import_react_native20.View,
3562
+ import_react_native21.View,
3462
3563
  {
3463
3564
  style: {
3464
3565
  flexDirection: "row",
@@ -3494,7 +3595,7 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3494
3595
  interactive: true,
3495
3596
  effect: "clear",
3496
3597
  children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3497
- import_react_native20.View,
3598
+ import_react_native21.View,
3498
3599
  {
3499
3600
  style: {
3500
3601
  width: 32,
@@ -3506,7 +3607,7 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3506
3607
  opacity: appId ? 1 : 0.5
3507
3608
  },
3508
3609
  children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3509
- import_react_native20.Pressable,
3610
+ import_react_native21.Pressable,
3510
3611
  {
3511
3612
  disabled: !appId,
3512
3613
  onPress: () => void handlePlay(),
@@ -3541,13 +3642,13 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3541
3642
  },
3542
3643
  keyboardShouldPersistTaps: "handled",
3543
3644
  children: [
3544
- 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)),
3545
3646
  error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { variant: "captionMuted", style: { marginTop: theme.spacing.lg }, children: "Failed to load comments." }) : null
3546
3647
  ]
3547
3648
  }
3548
3649
  ),
3549
3650
  /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3550
- import_react_native20.View,
3651
+ import_react_native21.View,
3551
3652
  {
3552
3653
  style: {
3553
3654
  position: "absolute",
@@ -3556,7 +3657,7 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3556
3657
  bottom: 0,
3557
3658
  paddingHorizontal: theme.spacing.lg,
3558
3659
  paddingTop: theme.spacing.sm,
3559
- 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,
3560
3661
  borderTopWidth: 1,
3561
3662
  borderTopColor: withAlpha(theme.colors.border, 0.1),
3562
3663
  backgroundColor: withAlpha(theme.colors.background, 0.8)
@@ -3570,7 +3671,7 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3570
3671
  useBottomSheetTextInput: true,
3571
3672
  onSend: async (text) => {
3572
3673
  await create(text);
3573
- import_react_native20.Keyboard.dismiss();
3674
+ import_react_native21.Keyboard.dismiss();
3574
3675
  }
3575
3676
  }
3576
3677
  )
@@ -3582,16 +3683,16 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3582
3683
  }
3583
3684
 
3584
3685
  // src/studio/ui/PreviewPanel.tsx
3585
- var import_react_native41 = require("react-native");
3686
+ var import_react_native42 = require("react-native");
3586
3687
 
3587
3688
  // src/components/preview/PreviewPage.tsx
3588
- var import_react_native21 = require("react-native");
3689
+ var import_react_native22 = require("react-native");
3589
3690
  var import_bottom_sheet4 = require("@gorhom/bottom-sheet");
3590
3691
  var import_jsx_runtime19 = require("react/jsx-runtime");
3591
3692
  function PreviewPage({ header, children, contentStyle }) {
3592
3693
  const theme = useTheme();
3593
- return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native21.View, { style: { flex: 1 }, children: [
3594
- 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,
3595
3696
  /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3596
3697
  import_bottom_sheet4.BottomSheetScrollView,
3597
3698
  {
@@ -3611,15 +3712,15 @@ function PreviewPage({ header, children, contentStyle }) {
3611
3712
  }
3612
3713
 
3613
3714
  // src/studio/ui/preview-panel/PreviewPanelHeader.tsx
3614
- var import_react_native24 = require("react-native");
3715
+ var import_react_native25 = require("react-native");
3615
3716
 
3616
3717
  // src/components/studio-sheet/StudioSheetHeader.tsx
3617
- var import_react_native22 = require("react-native");
3718
+ var import_react_native23 = require("react-native");
3618
3719
  var import_jsx_runtime20 = require("react/jsx-runtime");
3619
3720
  function StudioSheetHeader({ left, center, right, style }) {
3620
3721
  const theme = useTheme();
3621
3722
  return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
3622
- import_react_native22.View,
3723
+ import_react_native23.View,
3623
3724
  {
3624
3725
  style: [
3625
3726
  {
@@ -3632,9 +3733,9 @@ function StudioSheetHeader({ left, center, right, style }) {
3632
3733
  style
3633
3734
  ],
3634
3735
  children: [
3635
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native22.View, { style: { flexDirection: "row", alignItems: "center" }, children: left }),
3636
- /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native22.View, { style: { flex: 1, alignItems: "center" }, children: center }),
3637
- /* @__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 })
3638
3739
  ]
3639
3740
  }
3640
3741
  );
@@ -3642,7 +3743,7 @@ function StudioSheetHeader({ left, center, right, style }) {
3642
3743
 
3643
3744
  // src/components/studio-sheet/StudioSheetHeaderIconButton.tsx
3644
3745
  var React22 = __toESM(require("react"));
3645
- var import_react_native23 = require("react-native");
3746
+ var import_react_native24 = require("react-native");
3646
3747
  var import_liquid_glass5 = require("@callstack/liquid-glass");
3647
3748
  var import_jsx_runtime21 = require("react/jsx-runtime");
3648
3749
  function StudioSheetHeaderIconButton({
@@ -3661,14 +3762,14 @@ function StudioSheetHeaderIconButton({
3661
3762
  const glassFallbackBg = theme.scheme === "dark" ? "#18181B" : "#F6F6F6";
3662
3763
  const glassInnerBg = intent === "danger" ? theme.colors.danger : theme.colors.primary;
3663
3764
  const resolvedOpacity = disabled ? 0.6 : pressed ? 0.9 : 1;
3664
- 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)(
3665
3766
  import_liquid_glass5.LiquidGlassView,
3666
3767
  {
3667
3768
  style: [{ borderRadius: 100 }, !import_liquid_glass5.isLiquidGlassSupported && { backgroundColor: glassFallbackBg }],
3668
3769
  interactive: true,
3669
3770
  effect: "clear",
3670
3771
  children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3671
- import_react_native23.View,
3772
+ import_react_native24.View,
3672
3773
  {
3673
3774
  style: {
3674
3775
  width: size,
@@ -3680,7 +3781,7 @@ function StudioSheetHeaderIconButton({
3680
3781
  opacity: resolvedOpacity
3681
3782
  },
3682
3783
  children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3683
- import_react_native23.Pressable,
3784
+ import_react_native24.Pressable,
3684
3785
  {
3685
3786
  accessibilityRole: "button",
3686
3787
  accessibilityLabel,
@@ -3699,7 +3800,7 @@ function StudioSheetHeaderIconButton({
3699
3800
  )
3700
3801
  }
3701
3802
  ) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3702
- import_react_native23.View,
3803
+ import_react_native24.View,
3703
3804
  {
3704
3805
  style: {
3705
3806
  width: size,
@@ -3711,7 +3812,7 @@ function StudioSheetHeaderIconButton({
3711
3812
  opacity: resolvedOpacity
3712
3813
  },
3713
3814
  children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3714
- import_react_native23.Pressable,
3815
+ import_react_native24.Pressable,
3715
3816
  {
3716
3817
  accessibilityRole: "button",
3717
3818
  accessibilityLabel,
@@ -3738,7 +3839,7 @@ function PreviewPanelHeader({ isOwner, onClose, onNavigateHome, onGoToChat }) {
3738
3839
  {
3739
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,
3740
3841
  center: null,
3741
- 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: [
3742
3843
  isOwner ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3743
3844
  StudioSheetHeaderIconButton,
3744
3845
  {
@@ -3757,10 +3858,10 @@ function PreviewPanelHeader({ isOwner, onClose, onNavigateHome, onGoToChat }) {
3757
3858
  }
3758
3859
 
3759
3860
  // src/components/preview/PreviewHeroCard.tsx
3760
- var import_react_native26 = require("react-native");
3861
+ var import_react_native27 = require("react-native");
3761
3862
 
3762
3863
  // src/components/primitives/Surface.tsx
3763
- var import_react_native25 = require("react-native");
3864
+ var import_react_native26 = require("react-native");
3764
3865
  var import_jsx_runtime23 = require("react/jsx-runtime");
3765
3866
  function backgroundFor(variant, theme) {
3766
3867
  const { colors } = theme;
@@ -3779,7 +3880,7 @@ function backgroundFor(variant, theme) {
3779
3880
  function Surface({ variant = "surface", border = false, style, ...props }) {
3780
3881
  const theme = useTheme();
3781
3882
  return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3782
- import_react_native25.View,
3883
+ import_react_native26.View,
3783
3884
  {
3784
3885
  ...props,
3785
3886
  style: [
@@ -3835,11 +3936,11 @@ function PreviewHeroCard({
3835
3936
  },
3836
3937
  style
3837
3938
  ],
3838
- children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(import_react_native26.View, { style: { flex: 1 }, children: [
3839
- background ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native26.View, { style: { position: "absolute", inset: 0 }, children: background }) : null,
3840
- image ? /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(import_react_native26.View, { style: { position: "absolute", inset: 0 }, children: image }) : null,
3841
- 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,
3842
- 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
3843
3944
  ] })
3844
3945
  }
3845
3946
  );
@@ -3847,20 +3948,20 @@ function PreviewHeroCard({
3847
3948
 
3848
3949
  // src/components/preview/PreviewPlaceholder.tsx
3849
3950
  var React23 = __toESM(require("react"));
3850
- var import_react_native27 = require("react-native");
3951
+ var import_react_native28 = require("react-native");
3851
3952
  var import_expo_linear_gradient2 = require("expo-linear-gradient");
3852
3953
  var import_jsx_runtime26 = require("react/jsx-runtime");
3853
3954
  function PreviewPlaceholder({ visible, style }) {
3854
3955
  if (!visible) return null;
3855
- 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;
3856
3957
  React23.useEffect(() => {
3857
3958
  if (!visible) return;
3858
- const animation = import_react_native27.Animated.loop(
3859
- import_react_native27.Animated.sequence([
3860
- import_react_native27.Animated.timing(opacityAnim, { toValue: 1, duration: 1500, useNativeDriver: true }),
3861
- import_react_native27.Animated.timing(opacityAnim, { toValue: 2, duration: 1500, useNativeDriver: true }),
3862
- import_react_native27.Animated.timing(opacityAnim, { toValue: 3, duration: 1500, useNativeDriver: true }),
3863
- 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 })
3864
3965
  ])
3865
3966
  );
3866
3967
  animation.start();
@@ -3871,7 +3972,7 @@ function PreviewPlaceholder({ visible, style }) {
3871
3972
  const opacity3 = opacityAnim.interpolate({ inputRange: [0, 1, 2, 3], outputRange: [0, 0, 1, 0] });
3872
3973
  const opacity4 = opacityAnim.interpolate({ inputRange: [0, 1, 2, 3], outputRange: [0, 0, 0, 1] });
3873
3974
  return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(import_jsx_runtime26.Fragment, { children: [
3874
- /* @__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)(
3875
3976
  import_expo_linear_gradient2.LinearGradient,
3876
3977
  {
3877
3978
  colors: ["rgba(98, 0, 238, 0.45)", "rgba(168, 85, 247, 0.35)"],
@@ -3880,7 +3981,7 @@ function PreviewPlaceholder({ visible, style }) {
3880
3981
  style: { width: "100%", height: "100%" }
3881
3982
  }
3882
3983
  ) }),
3883
- /* @__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)(
3884
3985
  import_expo_linear_gradient2.LinearGradient,
3885
3986
  {
3886
3987
  colors: ["rgba(168, 85, 247, 0.45)", "rgba(139, 92, 246, 0.35)"],
@@ -3889,7 +3990,7 @@ function PreviewPlaceholder({ visible, style }) {
3889
3990
  style: { width: "100%", height: "100%" }
3890
3991
  }
3891
3992
  ) }),
3892
- /* @__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)(
3893
3994
  import_expo_linear_gradient2.LinearGradient,
3894
3995
  {
3895
3996
  colors: ["rgba(139, 92, 246, 0.45)", "rgba(126, 34, 206, 0.35)"],
@@ -3898,7 +3999,7 @@ function PreviewPlaceholder({ visible, style }) {
3898
3999
  style: { width: "100%", height: "100%" }
3899
4000
  }
3900
4001
  ) }),
3901
- /* @__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)(
3902
4003
  import_expo_linear_gradient2.LinearGradient,
3903
4004
  {
3904
4005
  colors: ["rgba(126, 34, 206, 0.45)", "rgba(98, 0, 238, 0.35)"],
@@ -3911,12 +4012,12 @@ function PreviewPlaceholder({ visible, style }) {
3911
4012
  }
3912
4013
 
3913
4014
  // src/components/preview/PreviewImage.tsx
3914
- var import_react_native28 = require("react-native");
4015
+ var import_react_native29 = require("react-native");
3915
4016
  var import_jsx_runtime27 = require("react/jsx-runtime");
3916
4017
  function PreviewImage({ uri, onLoad, style }) {
3917
4018
  if (!uri) return null;
3918
4019
  return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3919
- import_react_native28.Image,
4020
+ import_react_native29.Image,
3920
4021
  {
3921
4022
  source: { uri },
3922
4023
  resizeMode: "cover",
@@ -3927,7 +4028,7 @@ function PreviewImage({ uri, onLoad, style }) {
3927
4028
  }
3928
4029
 
3929
4030
  // src/components/preview/StatsBar.tsx
3930
- var import_react_native29 = require("react-native");
4031
+ var import_react_native30 = require("react-native");
3931
4032
  var import_liquid_glass6 = require("@callstack/liquid-glass");
3932
4033
  var import_lucide_react_native5 = require("lucide-react-native");
3933
4034
 
@@ -3960,7 +4061,7 @@ function StatsBar({
3960
4061
  const theme = useTheme();
3961
4062
  const statsBgColor = theme.scheme === "dark" ? "rgba(24, 24, 27, 0.5)" : "rgba(255, 255, 255, 0.5)";
3962
4063
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3963
- import_react_native29.View,
4064
+ import_react_native30.View,
3964
4065
  {
3965
4066
  style: [
3966
4067
  { position: "absolute", bottom: 12, width: "100%", paddingHorizontal: 12 },
@@ -3976,15 +4077,15 @@ function StatsBar({
3976
4077
  !import_liquid_glass6.isLiquidGlassSupported && { backgroundColor: statsBgColor }
3977
4078
  ],
3978
4079
  effect: "clear",
3979
- 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: [
3980
4081
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3981
- import_react_native29.Pressable,
4082
+ import_react_native30.Pressable,
3982
4083
  {
3983
4084
  disabled: !onPressLike,
3984
4085
  onPress: onPressLike,
3985
4086
  hitSlop: 8,
3986
4087
  style: { paddingVertical: 8 },
3987
- 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: [
3988
4089
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3989
4090
  import_lucide_react_native5.Heart,
3990
4091
  {
@@ -3994,7 +4095,7 @@ function StatsBar({
3994
4095
  fill: isLiked ? theme.colors.danger : "transparent"
3995
4096
  }
3996
4097
  ),
3997
- /* @__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 } }),
3998
4099
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3999
4100
  Text,
4000
4101
  {
@@ -4010,22 +4111,22 @@ function StatsBar({
4010
4111
  }
4011
4112
  ),
4012
4113
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
4013
- import_react_native29.Pressable,
4114
+ import_react_native30.Pressable,
4014
4115
  {
4015
4116
  disabled: !onPressComments,
4016
4117
  onPress: onPressComments,
4017
4118
  hitSlop: 8,
4018
4119
  style: { paddingVertical: 8 },
4019
- 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: [
4020
4121
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_lucide_react_native5.MessageCircle, { size: 16, strokeWidth: 2.5, color: "#FFFFFF" }),
4021
- /* @__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 } }),
4022
4123
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Text, { variant: "caption", style: { color: "#FFFFFF", fontWeight: theme.typography.fontWeight.bold }, children: commentCount })
4023
4124
  ] })
4024
4125
  }
4025
4126
  ),
4026
- /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_react_native29.View, { style: { flexDirection: "row", alignItems: "center", paddingVertical: 8 }, children: [
4027
- /* @__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" }) }),
4028
- /* @__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 } }),
4029
4130
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Text, { variant: "caption", style: { color: "#FFFFFF", fontWeight: theme.typography.fontWeight.bold }, children: forkCount })
4030
4131
  ] })
4031
4132
  ] })
@@ -4036,7 +4137,7 @@ function StatsBar({
4036
4137
  }
4037
4138
 
4038
4139
  // src/components/preview/PreviewStatusBadge.tsx
4039
- var import_react_native30 = require("react-native");
4140
+ var import_react_native31 = require("react-native");
4040
4141
  var import_lucide_react_native6 = require("lucide-react-native");
4041
4142
 
4042
4143
  // src/data/apps/types.ts
@@ -4081,7 +4182,7 @@ function PreviewStatusBadge({ status }) {
4081
4182
  const IconComp = STATUS_ICON[status];
4082
4183
  const label = APP_STATUS_LABEL[status] ?? status;
4083
4184
  return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
4084
- import_react_native30.View,
4185
+ import_react_native31.View,
4085
4186
  {
4086
4187
  style: {
4087
4188
  flexDirection: "row",
@@ -4134,10 +4235,10 @@ function PreviewHeroSection({
4134
4235
  }
4135
4236
 
4136
4237
  // src/studio/ui/preview-panel/PreviewMetaSection.tsx
4137
- var import_react_native32 = require("react-native");
4238
+ var import_react_native33 = require("react-native");
4138
4239
 
4139
4240
  // src/components/preview/PreviewMetaRow.tsx
4140
- var import_react_native31 = require("react-native");
4241
+ var import_react_native32 = require("react-native");
4141
4242
  var import_jsx_runtime32 = require("react/jsx-runtime");
4142
4243
  function PreviewMetaRow({
4143
4244
  avatarUri,
@@ -4149,10 +4250,10 @@ function PreviewMetaRow({
4149
4250
  style
4150
4251
  }) {
4151
4252
  const theme = useTheme();
4152
- return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(import_react_native31.View, { style: [{ alignSelf: "stretch" }, style], children: [
4153
- /* @__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: [
4154
4255
  /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Avatar, { uri: avatarUri, name: creatorName, size: 24, style: { marginRight: theme.spacing.sm } }),
4155
- /* @__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: [
4156
4257
  /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4157
4258
  Text,
4158
4259
  {
@@ -4167,9 +4268,9 @@ function PreviewMetaRow({
4167
4268
  children: title
4168
4269
  }
4169
4270
  ),
4170
- 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
4171
4272
  ] }),
4172
- 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
4173
4274
  ] }),
4174
4275
  subtitle ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4175
4276
  Text,
@@ -4225,9 +4326,9 @@ function PreviewMetaSection({ app, isOwner, creator, downloadsCount }) {
4225
4326
  subtitle: app.description,
4226
4327
  avatarUri: (creator == null ? void 0 : creator.avatar) ?? null,
4227
4328
  creatorName: (creator == null ? void 0 : creator.name) ?? null,
4228
- 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,
4229
4330
  rightMetric: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
4230
- import_react_native32.View,
4331
+ import_react_native33.View,
4231
4332
  {
4232
4333
  style: {
4233
4334
  flexDirection: "row",
@@ -4261,10 +4362,10 @@ function PreviewMetaSection({ app, isOwner, creator, downloadsCount }) {
4261
4362
  }
4262
4363
 
4263
4364
  // src/studio/ui/preview-panel/PreviewCustomizeSection.tsx
4264
- var import_react_native34 = require("react-native");
4365
+ var import_react_native35 = require("react-native");
4265
4366
 
4266
4367
  // src/studio/ui/preview-panel/PressableCardRow.tsx
4267
- var import_react_native33 = require("react-native");
4368
+ var import_react_native34 = require("react-native");
4268
4369
  var import_jsx_runtime34 = require("react/jsx-runtime");
4269
4370
  function PressableCardRow({
4270
4371
  accessibilityLabel,
@@ -4277,20 +4378,20 @@ function PressableCardRow({
4277
4378
  style
4278
4379
  }) {
4279
4380
  return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4280
- import_react_native33.Pressable,
4381
+ import_react_native34.Pressable,
4281
4382
  {
4282
4383
  accessibilityRole: "button",
4283
4384
  accessibilityLabel,
4284
4385
  disabled,
4285
4386
  onPress,
4286
4387
  style: ({ pressed }) => ({ opacity: disabled ? 0.6 : pressed ? 0.85 : 1 }),
4287
- 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: [
4288
4389
  left,
4289
- /* @__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: [
4290
4391
  title,
4291
4392
  subtitle ? subtitle : null
4292
4393
  ] }),
4293
- 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
4294
4395
  ] }) })
4295
4396
  }
4296
4397
  );
@@ -4332,7 +4433,7 @@ function PreviewCustomizeSection({
4332
4433
  return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
4333
4434
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(SectionTitle, { children: "Customize" }),
4334
4435
  showProcessing ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
4335
- import_react_native34.View,
4436
+ import_react_native35.View,
4336
4437
  {
4337
4438
  style: {
4338
4439
  flexDirection: "row",
@@ -4346,7 +4447,7 @@ function PreviewCustomizeSection({
4346
4447
  },
4347
4448
  children: [
4348
4449
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4349
- import_react_native34.View,
4450
+ import_react_native35.View,
4350
4451
  {
4351
4452
  style: {
4352
4453
  width: 40,
@@ -4357,10 +4458,10 @@ function PreviewCustomizeSection({
4357
4458
  backgroundColor: withAlpha(theme.colors.warning, 0.1),
4358
4459
  marginRight: theme.spacing.lg
4359
4460
  },
4360
- 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" })
4361
4462
  }
4362
4463
  ),
4363
- /* @__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: [
4364
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" }),
4365
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) })
4366
4467
  ] })
@@ -4381,7 +4482,7 @@ function PreviewCustomizeSection({
4381
4482
  marginBottom: theme.spacing.sm
4382
4483
  },
4383
4484
  left: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4384
- import_react_native34.View,
4485
+ import_react_native35.View,
4385
4486
  {
4386
4487
  style: {
4387
4488
  width: 40,
@@ -4414,7 +4515,7 @@ function PreviewCustomizeSection({
4414
4515
  marginBottom: theme.spacing.sm
4415
4516
  },
4416
4517
  left: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4417
- import_react_native34.View,
4518
+ import_react_native35.View,
4418
4519
  {
4419
4520
  style: {
4420
4521
  width: 40,
@@ -4438,16 +4539,16 @@ function PreviewCustomizeSection({
4438
4539
 
4439
4540
  // src/studio/ui/preview-panel/PreviewCollaborateSection.tsx
4440
4541
  var React29 = __toESM(require("react"));
4441
- var import_react_native40 = require("react-native");
4542
+ var import_react_native41 = require("react-native");
4442
4543
  var import_lucide_react_native9 = require("lucide-react-native");
4443
4544
 
4444
4545
  // src/components/merge-requests/MergeRequestStatusCard.tsx
4445
4546
  var React25 = __toESM(require("react"));
4446
- var import_react_native36 = require("react-native");
4547
+ var import_react_native37 = require("react-native");
4447
4548
  var import_lucide_react_native7 = require("lucide-react-native");
4448
4549
 
4449
4550
  // src/components/primitives/MarkdownText.tsx
4450
- var import_react_native35 = require("react-native");
4551
+ var import_react_native36 = require("react-native");
4451
4552
  var import_react_native_markdown_display = __toESM(require("react-native-markdown-display"));
4452
4553
  var import_jsx_runtime37 = require("react/jsx-runtime");
4453
4554
  function MarkdownText({ markdown, variant = "chat", bodyColor, style }) {
@@ -4460,7 +4561,7 @@ function MarkdownText({ markdown, variant = "chat", bodyColor, style }) {
4460
4561
  const codeTextColor = isDark ? "#FFFFFF" : theme.colors.text;
4461
4562
  const paragraphBottom = variant === "mergeRequest" ? 8 : 6;
4462
4563
  const baseLineHeight = variant === "mergeRequest" ? 22 : 20;
4463
- 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)(
4464
4565
  import_react_native_markdown_display.default,
4465
4566
  {
4466
4567
  style: {
@@ -4473,7 +4574,7 @@ function MarkdownText({ markdown, variant = "chat", bodyColor, style }) {
4473
4574
  paddingHorizontal: variant === "mergeRequest" ? 6 : 4,
4474
4575
  paddingVertical: variant === "mergeRequest" ? 2 : 0,
4475
4576
  borderRadius: variant === "mergeRequest" ? 6 : 4,
4476
- fontFamily: import_react_native35.Platform.OS === "ios" ? "Menlo" : "monospace",
4577
+ fontFamily: import_react_native36.Platform.OS === "ios" ? "Menlo" : "monospace",
4477
4578
  fontSize: 13
4478
4579
  },
4479
4580
  code_block: {
@@ -4585,15 +4686,15 @@ function MergeRequestStatusCard({
4585
4686
  const createdIso = toIsoString(mergeRequest.createdAt ?? null);
4586
4687
  const headerTimeAgo = updatedIso ? formatTimeAgo(updatedIso) : "";
4587
4688
  const createdTimeAgo = createdIso ? formatTimeAgo(createdIso) : "";
4588
- 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;
4589
4690
  React25.useEffect(() => {
4590
- import_react_native36.Animated.timing(rotate, {
4691
+ import_react_native37.Animated.timing(rotate, {
4591
4692
  toValue: expanded ? 1 : 0,
4592
4693
  duration: 200,
4593
4694
  useNativeDriver: true
4594
4695
  }).start();
4595
4696
  }, [expanded, rotate]);
4596
- 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)(
4597
4698
  Card,
4598
4699
  {
4599
4700
  padded: false,
@@ -4606,10 +4707,10 @@ function MergeRequestStatusCard({
4606
4707
  style
4607
4708
  ],
4608
4709
  children: [
4609
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react_native36.View, { style: { flexDirection: "row", alignItems: "center", gap: theme.spacing.lg }, children: [
4610
- /* @__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 }) }),
4611
- /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react_native36.View, { style: { flex: 1, minWidth: 0 }, children: [
4612
- /* @__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: [
4613
4714
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4614
4715
  Text,
4615
4716
  {
@@ -4628,8 +4729,8 @@ function MergeRequestStatusCard({
4628
4729
  ] }),
4629
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" })
4630
4731
  ] }),
4631
- headerRight ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_native36.View, { children: headerRight }) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4632
- 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,
4633
4734
  {
4634
4735
  style: {
4635
4736
  transform: [
@@ -4642,7 +4743,7 @@ function MergeRequestStatusCard({
4642
4743
  }
4643
4744
  )
4644
4745
  ] }),
4645
- 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: [
4646
4747
  /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4647
4748
  Text,
4648
4749
  {
@@ -4678,16 +4779,16 @@ function MergeRequestStatusCard({
4678
4779
 
4679
4780
  // src/components/merge-requests/ReviewMergeRequestCarousel.tsx
4680
4781
  var React28 = __toESM(require("react"));
4681
- var import_react_native39 = require("react-native");
4782
+ var import_react_native40 = require("react-native");
4682
4783
 
4683
4784
  // src/components/merge-requests/ReviewMergeRequestCard.tsx
4684
4785
  var React27 = __toESM(require("react"));
4685
- var import_react_native38 = require("react-native");
4786
+ var import_react_native39 = require("react-native");
4686
4787
  var import_lucide_react_native8 = require("lucide-react-native");
4687
4788
 
4688
4789
  // src/components/merge-requests/ReviewMergeRequestActionButton.tsx
4689
4790
  var React26 = __toESM(require("react"));
4690
- var import_react_native37 = require("react-native");
4791
+ var import_react_native38 = require("react-native");
4691
4792
  var import_jsx_runtime39 = require("react/jsx-runtime");
4692
4793
  function ReviewMergeRequestActionButton({
4693
4794
  accessibilityLabel,
@@ -4704,7 +4805,7 @@ function ReviewMergeRequestActionButton({
4704
4805
  const paddingVertical = iconOnly ? 0 : 8;
4705
4806
  const opacity = disabled ? 0.5 : pressed ? 0.9 : 1;
4706
4807
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4707
- import_react_native37.View,
4808
+ import_react_native38.View,
4708
4809
  {
4709
4810
  style: {
4710
4811
  width,
@@ -4719,7 +4820,7 @@ function ReviewMergeRequestActionButton({
4719
4820
  justifyContent: "center"
4720
4821
  },
4721
4822
  children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4722
- import_react_native37.Pressable,
4823
+ import_react_native38.Pressable,
4723
4824
  {
4724
4825
  accessibilityRole: "button",
4725
4826
  accessibilityLabel,
@@ -4761,12 +4862,12 @@ function ReviewMergeRequestCard({
4761
4862
  const theme = useTheme();
4762
4863
  const status = React27.useMemo(() => getMergeRequestStatusDisplay(mr.status), [mr.status]);
4763
4864
  const canAct = mr.status === "open";
4764
- 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;
4765
4866
  React27.useEffect(() => {
4766
- 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();
4767
4868
  }, [isExpanded, rotate]);
4768
4869
  const position = total > 1 ? `${index + 1}/${total}` : "Merge request";
4769
- 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)(
4770
4871
  Card,
4771
4872
  {
4772
4873
  padded: false,
@@ -4779,9 +4880,9 @@ function ReviewMergeRequestCard({
4779
4880
  }
4780
4881
  ],
4781
4882
  children: [
4782
- /* @__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: [
4783
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 }),
4784
- /* @__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: [
4785
4886
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4786
4887
  Text,
4787
4888
  {
@@ -4797,7 +4898,7 @@ function ReviewMergeRequestCard({
4797
4898
  ] })
4798
4899
  ] }),
4799
4900
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4800
- import_react_native38.Animated.View,
4901
+ import_react_native39.Animated.View,
4801
4902
  {
4802
4903
  style: {
4803
4904
  transform: [{ rotate: rotate.interpolate({ inputRange: [0, 1], outputRange: ["0deg", "180deg"] }) }]
@@ -4806,7 +4907,7 @@ function ReviewMergeRequestCard({
4806
4907
  }
4807
4908
  )
4808
4909
  ] }),
4809
- 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: [
4810
4911
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4811
4912
  Text,
4812
4913
  {
@@ -4824,9 +4925,9 @@ function ReviewMergeRequestCard({
4824
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..." }),
4825
4926
  mr.description ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(MarkdownText, { markdown: mr.description, variant: "mergeRequest" }) : null
4826
4927
  ] }) : null,
4827
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_native38.View, { style: { height: 1, backgroundColor: withAlpha(theme.colors.borderStrong, 0.5), marginTop: 12, marginBottom: 12 } }),
4828
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, children: [
4829
- /* @__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: [
4830
4931
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4831
4932
  ReviewMergeRequestActionButton,
4832
4933
  {
@@ -4835,7 +4936,7 @@ function ReviewMergeRequestCard({
4835
4936
  disabled: !canAct || isAnyProcessing,
4836
4937
  onPress: onReject,
4837
4938
  iconOnly: !isExpanded,
4838
- 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: [
4839
4940
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react_native8.X, { size: 18, color: "#FFFFFF" }),
4840
4941
  isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 13, color: "#FFFFFF", fontWeight: theme.typography.fontWeight.semibold }, children: "Reject" }) : null
4841
4942
  ] })
@@ -4849,10 +4950,10 @@ function ReviewMergeRequestCard({
4849
4950
  disabled: !canAct || isAnyProcessing,
4850
4951
  onPress: onApprove,
4851
4952
  iconOnly: !isExpanded,
4852
- children: isProcessing ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
4853
- /* @__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" }),
4854
4955
  isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 13, color: "#FFFFFF", fontWeight: theme.typography.fontWeight.semibold }, children: "Processing" }) : null
4855
- ] }) : /* @__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: [
4856
4957
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react_native8.Check, { size: 18, color: "#FFFFFF" }),
4857
4958
  isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 13, color: "#FFFFFF", fontWeight: theme.typography.fontWeight.semibold }, children: "Approve" }) : null
4858
4959
  ] })
@@ -4867,7 +4968,7 @@ function ReviewMergeRequestCard({
4867
4968
  disabled: isBuilding || isTestingThis,
4868
4969
  onPress: onTest,
4869
4970
  iconOnly: !isExpanded,
4870
- 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: [
4871
4972
  /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react_native8.Play, { size: 14, color: theme.colors.text }),
4872
4973
  isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 13, color: theme.colors.text, fontWeight: theme.typography.fontWeight.semibold }, children: "Test" }) : null
4873
4974
  ] })
@@ -4893,32 +4994,32 @@ function ReviewMergeRequestCarousel({
4893
4994
  style
4894
4995
  }) {
4895
4996
  const theme = useTheme();
4896
- const { width } = (0, import_react_native39.useWindowDimensions)();
4997
+ const { width } = (0, import_react_native40.useWindowDimensions)();
4897
4998
  const [expanded, setExpanded] = React28.useState({});
4898
- 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;
4899
5000
  const peekAmount = 24;
4900
5001
  const gap = 16;
4901
5002
  const cardWidth = React28.useMemo(() => Math.max(1, width - theme.spacing.lg * 2 - peekAmount), [peekAmount, theme.spacing.lg, width]);
4902
5003
  const snapInterval = cardWidth + gap;
4903
5004
  const dotColor = theme.scheme === "dark" ? "#FFFFFF" : "#000000";
4904
5005
  if (mergeRequests.length === 0) return null;
4905
- 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: [
4906
5007
  /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
4907
- import_react_native39.FlatList,
5008
+ import_react_native40.FlatList,
4908
5009
  {
4909
5010
  horizontal: true,
4910
5011
  data: mergeRequests,
4911
5012
  keyExtractor: (mr) => mr.id,
4912
5013
  showsHorizontalScrollIndicator: false,
4913
5014
  contentContainerStyle: { paddingHorizontal: theme.spacing.lg, paddingVertical: theme.spacing.sm },
4914
- 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 } }),
4915
5016
  snapToAlignment: "start",
4916
5017
  decelerationRate: "fast",
4917
5018
  snapToInterval: snapInterval,
4918
5019
  disableIntervalMomentum: true,
4919
5020
  style: { paddingRight: peekAmount },
4920
- ListFooterComponent: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_native39.View, { style: { width: peekAmount } }),
4921
- 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 } } }], {
4922
5023
  useNativeDriver: false
4923
5024
  }),
4924
5025
  scrollEventThrottle: 16,
@@ -4929,7 +5030,7 @@ function ReviewMergeRequestCarousel({
4929
5030
  const isProcessing = Boolean(processingMrId && processingMrId === item.id);
4930
5031
  const isAnyProcessing = Boolean(processingMrId);
4931
5032
  const isTestingThis = Boolean(testingMrId && testingMrId === item.id);
4932
- 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)(
4933
5034
  ReviewMergeRequestCard,
4934
5035
  {
4935
5036
  mr: item,
@@ -4950,7 +5051,7 @@ function ReviewMergeRequestCarousel({
4950
5051
  }
4951
5052
  }
4952
5053
  ),
4953
- 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) => {
4954
5055
  const inputRange = [(index - 1) * snapInterval, index * snapInterval, (index + 1) * snapInterval];
4955
5056
  const scale = carouselScrollX.interpolate({
4956
5057
  inputRange,
@@ -4963,7 +5064,7 @@ function ReviewMergeRequestCarousel({
4963
5064
  extrapolate: "clamp"
4964
5065
  });
4965
5066
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
4966
- import_react_native39.Animated.View,
5067
+ import_react_native40.Animated.View,
4967
5068
  {
4968
5069
  style: {
4969
5070
  width: 8,
@@ -5024,7 +5125,7 @@ function PreviewCollaborateSection({
5024
5125
  accessibilityLabel: "Submit merge request",
5025
5126
  disabled: submittingMr,
5026
5127
  onPress: () => {
5027
- import_react_native40.Alert.alert(
5128
+ import_react_native41.Alert.alert(
5028
5129
  "Submit Merge Request",
5029
5130
  "Are you sure you want to submit your changes to the original app?",
5030
5131
  [
@@ -5050,7 +5151,7 @@ function PreviewCollaborateSection({
5050
5151
  marginBottom: theme.spacing.sm
5051
5152
  },
5052
5153
  left: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5053
- import_react_native40.View,
5154
+ import_react_native41.View,
5054
5155
  {
5055
5156
  style: {
5056
5157
  width: 40,
@@ -5061,7 +5162,7 @@ function PreviewCollaborateSection({
5061
5162
  backgroundColor: withAlpha("#03DAC6", 0.1),
5062
5163
  marginRight: theme.spacing.lg
5063
5164
  },
5064
- 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" })
5065
5166
  }
5066
5167
  ),
5067
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" }),
@@ -5099,7 +5200,7 @@ function PreviewCollaborateSection({
5099
5200
  children: "History"
5100
5201
  }
5101
5202
  ),
5102
- 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))
5103
5204
  ] }) : null
5104
5205
  ] });
5105
5206
  }
@@ -5423,9 +5524,9 @@ function PreviewPanel({
5423
5524
  });
5424
5525
  const header = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(PreviewPanelHeader, { isOwner, onClose, onNavigateHome, onGoToChat });
5425
5526
  if (loading || !app) {
5426
- 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: [
5427
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_react_native41.ActivityIndicator, {}),
5428
- /* @__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 } }),
5429
5530
  /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { variant: "bodyMuted", children: "Loading app\u2026" })
5430
5531
  ] }) });
5431
5532
  }
@@ -5482,20 +5583,20 @@ function PreviewPanel({
5482
5583
 
5483
5584
  // src/studio/ui/ChatPanel.tsx
5484
5585
  var React36 = __toESM(require("react"));
5485
- var import_react_native49 = require("react-native");
5586
+ var import_react_native50 = require("react-native");
5486
5587
 
5487
5588
  // src/components/chat/ChatPage.tsx
5488
5589
  var React34 = __toESM(require("react"));
5489
- var import_react_native45 = require("react-native");
5590
+ var import_react_native46 = require("react-native");
5490
5591
  var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
5491
5592
 
5492
5593
  // src/components/chat/ChatMessageList.tsx
5493
5594
  var React33 = __toESM(require("react"));
5494
- var import_react_native44 = require("react-native");
5595
+ var import_react_native45 = require("react-native");
5495
5596
  var import_bottom_sheet5 = require("@gorhom/bottom-sheet");
5496
5597
 
5497
5598
  // src/components/chat/ChatMessageBubble.tsx
5498
- var import_react_native42 = require("react-native");
5599
+ var import_react_native43 = require("react-native");
5499
5600
  var import_lucide_react_native10 = require("lucide-react-native");
5500
5601
  var import_jsx_runtime44 = require("react/jsx-runtime");
5501
5602
  function ChatMessageBubble({ message, renderContent, style }) {
@@ -5511,7 +5612,7 @@ function ChatMessageBubble({ message, renderContent, style }) {
5511
5612
  const bubbleVariant = isHuman ? "surface" : "surfaceRaised";
5512
5613
  const cornerStyle = isHuman ? { borderTopRightRadius: 0 } : { borderTopLeftRadius: 0 };
5513
5614
  const bodyColor = metaStatus === "success" ? theme.colors.success : metaStatus === "error" ? theme.colors.danger : void 0;
5514
- 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)(
5515
5616
  Surface,
5516
5617
  {
5517
5618
  variant: bubbleVariant,
@@ -5526,10 +5627,10 @@ function ChatMessageBubble({ message, renderContent, style }) {
5526
5627
  },
5527
5628
  cornerStyle
5528
5629
  ],
5529
- 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: [
5530
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,
5531
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,
5532
- /* @__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 }) })
5533
5634
  ] })
5534
5635
  }
5535
5636
  ) });
@@ -5537,23 +5638,23 @@ function ChatMessageBubble({ message, renderContent, style }) {
5537
5638
 
5538
5639
  // src/components/chat/TypingIndicator.tsx
5539
5640
  var React32 = __toESM(require("react"));
5540
- var import_react_native43 = require("react-native");
5641
+ var import_react_native44 = require("react-native");
5541
5642
  var import_jsx_runtime45 = require("react/jsx-runtime");
5542
5643
  function TypingIndicator({ style }) {
5543
5644
  const theme = useTheme();
5544
5645
  const dotColor = theme.colors.textSubtle;
5545
5646
  const anims = React32.useMemo(
5546
- () => [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)],
5547
5648
  []
5548
5649
  );
5549
5650
  React32.useEffect(() => {
5550
5651
  const loops = [];
5551
5652
  anims.forEach((a, idx) => {
5552
- const seq = import_react_native43.Animated.sequence([
5553
- import_react_native43.Animated.timing(a, { toValue: 1, duration: 420, useNativeDriver: true, delay: idx * 140 }),
5554
- 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 })
5555
5656
  ]);
5556
- const loop = import_react_native43.Animated.loop(seq);
5657
+ const loop = import_react_native44.Animated.loop(seq);
5557
5658
  loops.push(loop);
5558
5659
  loop.start();
5559
5660
  });
@@ -5561,8 +5662,8 @@ function TypingIndicator({ style }) {
5561
5662
  loops.forEach((l) => l.stop());
5562
5663
  };
5563
5664
  }, [anims]);
5564
- 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)(
5565
- 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,
5566
5667
  {
5567
5668
  style: {
5568
5669
  width: 8,
@@ -5571,7 +5672,7 @@ function TypingIndicator({ style }) {
5571
5672
  marginHorizontal: 3,
5572
5673
  backgroundColor: dotColor,
5573
5674
  opacity: a,
5574
- 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) }]
5575
5676
  }
5576
5677
  },
5577
5678
  i
@@ -5595,19 +5696,19 @@ var ChatMessageList = React33.forwardRef(
5595
5696
  const nearBottomRef = React33.useRef(true);
5596
5697
  const initialScrollDoneRef = React33.useRef(false);
5597
5698
  const lastMessageIdRef = React33.useRef(null);
5699
+ const data = React33.useMemo(() => {
5700
+ return [...messages].reverse();
5701
+ }, [messages]);
5598
5702
  const scrollToBottom = React33.useCallback((options) => {
5599
5703
  var _a;
5600
5704
  const animated = (options == null ? void 0 : options.animated) ?? true;
5601
- (_a = listRef.current) == null ? void 0 : _a.scrollToEnd({ animated });
5705
+ (_a = listRef.current) == null ? void 0 : _a.scrollToOffset({ offset: 0, animated });
5602
5706
  }, []);
5603
5707
  React33.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
5604
5708
  const handleScroll = React33.useCallback(
5605
5709
  (e) => {
5606
5710
  const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;
5607
- const distanceFromBottom = Math.max(
5608
- contentSize.height - Math.max(bottomInset, 0) - (contentOffset.y + layoutMeasurement.height),
5609
- 0
5610
- );
5711
+ const distanceFromBottom = Math.max(contentOffset.y - Math.max(bottomInset, 0), 0);
5611
5712
  const isNear = distanceFromBottom <= nearBottomThreshold;
5612
5713
  if (nearBottomRef.current !== isNear) {
5613
5714
  nearBottomRef.current = isNear;
@@ -5616,15 +5717,6 @@ var ChatMessageList = React33.forwardRef(
5616
5717
  },
5617
5718
  [bottomInset, nearBottomThreshold, onNearBottomChange]
5618
5719
  );
5619
- React33.useEffect(() => {
5620
- var _a;
5621
- if (initialScrollDoneRef.current) return;
5622
- if (messages.length === 0) return;
5623
- initialScrollDoneRef.current = true;
5624
- lastMessageIdRef.current = ((_a = messages[messages.length - 1]) == null ? void 0 : _a.id) ?? null;
5625
- const id = requestAnimationFrame(() => scrollToBottom({ animated: false }));
5626
- return () => cancelAnimationFrame(id);
5627
- }, [messages, scrollToBottom]);
5628
5720
  React33.useEffect(() => {
5629
5721
  if (!initialScrollDoneRef.current) return;
5630
5722
  const lastId = messages.length > 0 ? messages[messages.length - 1].id : null;
@@ -5642,35 +5734,37 @@ var ChatMessageList = React33.forwardRef(
5642
5734
  }
5643
5735
  return void 0;
5644
5736
  }, [showTypingIndicator, scrollToBottom]);
5645
- React33.useEffect(() => {
5646
- if (!initialScrollDoneRef.current) return;
5647
- if (!nearBottomRef.current) return;
5648
- const id = requestAnimationFrame(() => scrollToBottom({ animated: false }));
5649
- return () => cancelAnimationFrame(id);
5650
- }, [bottomInset, scrollToBottom]);
5651
5737
  return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
5652
5738
  import_bottom_sheet5.BottomSheetFlatList,
5653
5739
  {
5654
5740
  ref: listRef,
5655
- data: messages,
5741
+ inverted: true,
5742
+ data,
5656
5743
  keyExtractor: (m) => m.id,
5657
- keyboardDismissMode: import_react_native44.Platform.OS === "ios" ? "interactive" : "on-drag",
5658
5744
  keyboardShouldPersistTaps: "handled",
5659
5745
  onScroll: handleScroll,
5660
5746
  scrollEventThrottle: 16,
5661
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
+ },
5662
5756
  contentContainerStyle: [
5663
5757
  {
5664
5758
  paddingHorizontal: theme.spacing.lg,
5665
- paddingTop: theme.spacing.sm,
5666
- paddingBottom: theme.spacing.sm
5759
+ paddingVertical: theme.spacing.sm
5667
5760
  },
5668
5761
  contentStyle
5669
5762
  ],
5670
- 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 }) }),
5671
- ListFooterComponent: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_react_native44.View, { children: [
5672
- 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,
5673
- 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
5674
5768
  ] })
5675
5769
  }
5676
5770
  );
@@ -5689,6 +5783,7 @@ function ChatPage({
5689
5783
  composer,
5690
5784
  overlay,
5691
5785
  style,
5786
+ composerHorizontalPadding,
5692
5787
  onNearBottomChange,
5693
5788
  listRef
5694
5789
  }) {
@@ -5697,15 +5792,15 @@ function ChatPage({
5697
5792
  const [composerHeight, setComposerHeight] = React34.useState(0);
5698
5793
  const [keyboardVisible, setKeyboardVisible] = React34.useState(false);
5699
5794
  React34.useEffect(() => {
5700
- if (import_react_native45.Platform.OS !== "ios") return;
5701
- const show = import_react_native45.Keyboard.addListener("keyboardWillShow", () => setKeyboardVisible(true));
5702
- 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));
5703
5798
  return () => {
5704
5799
  show.remove();
5705
5800
  hide.remove();
5706
5801
  };
5707
5802
  }, []);
5708
- const footerBottomPadding = import_react_native45.Platform.OS === "ios" ? keyboardVisible ? 0 : insets.bottom : insets.bottom + 10;
5803
+ const footerBottomPadding = import_react_native46.Platform.OS === "ios" ? keyboardVisible ? 0 : insets.bottom : insets.bottom + 10;
5709
5804
  const overlayBottom = composerHeight + footerBottomPadding + theme.spacing.lg;
5710
5805
  const bottomInset = composerHeight + footerBottomPadding + theme.spacing.xl;
5711
5806
  const resolvedOverlay = React34.useMemo(() => {
@@ -5717,12 +5812,12 @@ function ChatPage({
5717
5812
  style: [prevStyle, { bottom: overlayBottom }]
5718
5813
  });
5719
5814
  }, [overlay, overlayBottom]);
5720
- return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_react_native45.View, { style: [{ flex: 1 }, style], children: [
5721
- header ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react_native45.View, { children: header }) : null,
5722
- topBanner ? /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react_native45.View, { style: { paddingHorizontal: theme.spacing.lg, paddingTop: theme.spacing.sm }, children: topBanner }) : null,
5723
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(import_react_native45.View, { style: { flex: 1 }, children: [
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: [
5724
5819
  /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
5725
- import_react_native45.View,
5820
+ import_react_native46.View,
5726
5821
  {
5727
5822
  style: { flex: 1 },
5728
5823
  children: [
@@ -5742,14 +5837,14 @@ function ChatPage({
5742
5837
  }
5743
5838
  ),
5744
5839
  /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
5745
- import_react_native45.View,
5840
+ import_react_native46.View,
5746
5841
  {
5747
5842
  style: {
5748
5843
  position: "absolute",
5749
5844
  left: 0,
5750
5845
  right: 0,
5751
5846
  bottom: 0,
5752
- paddingHorizontal: theme.spacing.lg,
5847
+ paddingHorizontal: composerHorizontalPadding ?? theme.spacing.md,
5753
5848
  paddingTop: theme.spacing.sm,
5754
5849
  paddingBottom: footerBottomPadding
5755
5850
  },
@@ -5769,7 +5864,7 @@ function ChatPage({
5769
5864
 
5770
5865
  // src/components/chat/ScrollToBottomButton.tsx
5771
5866
  var React35 = __toESM(require("react"));
5772
- var import_react_native46 = require("react-native");
5867
+ var import_react_native47 = require("react-native");
5773
5868
  var import_react_native_reanimated2 = __toESM(require("react-native-reanimated"));
5774
5869
  var import_jsx_runtime48 = require("react/jsx-runtime");
5775
5870
  function ScrollToBottomButton({ visible, onPress, children, style }) {
@@ -5800,7 +5895,7 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
5800
5895
  animStyle
5801
5896
  ],
5802
5897
  children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
5803
- import_react_native46.View,
5898
+ import_react_native47.View,
5804
5899
  {
5805
5900
  style: {
5806
5901
  width: 44,
@@ -5819,7 +5914,7 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
5819
5914
  opacity: pressed ? 0.85 : 1
5820
5915
  },
5821
5916
  children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
5822
- import_react_native46.Pressable,
5917
+ import_react_native47.Pressable,
5823
5918
  {
5824
5919
  onPress,
5825
5920
  onPressIn: () => setPressed(true),
@@ -5836,10 +5931,10 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
5836
5931
  }
5837
5932
 
5838
5933
  // src/components/chat/ChatHeader.tsx
5839
- var import_react_native47 = require("react-native");
5934
+ var import_react_native48 = require("react-native");
5840
5935
  var import_jsx_runtime49 = require("react/jsx-runtime");
5841
5936
  function ChatHeader({ left, right, center, style }) {
5842
- const flattenedStyle = import_react_native47.StyleSheet.flatten([
5937
+ const flattenedStyle = import_react_native48.StyleSheet.flatten([
5843
5938
  {
5844
5939
  paddingTop: 0
5845
5940
  },
@@ -5857,7 +5952,7 @@ function ChatHeader({ left, right, center, style }) {
5857
5952
  }
5858
5953
 
5859
5954
  // src/components/chat/ForkNoticeBanner.tsx
5860
- var import_react_native48 = require("react-native");
5955
+ var import_react_native49 = require("react-native");
5861
5956
  var import_jsx_runtime50 = require("react/jsx-runtime");
5862
5957
  function ForkNoticeBanner({ isOwner = true, title, description, style }) {
5863
5958
  const theme = useTheme();
@@ -5878,7 +5973,7 @@ function ForkNoticeBanner({ isOwner = true, title, description, style }) {
5878
5973
  },
5879
5974
  style
5880
5975
  ],
5881
- 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: [
5882
5977
  /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
5883
5978
  Text,
5884
5979
  {
@@ -5954,11 +6049,11 @@ function ChatPanel({
5954
6049
  const header = /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
5955
6050
  ChatHeader,
5956
6051
  {
5957
- 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: [
5958
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" }) }),
5959
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
5960
6055
  ] }),
5961
- 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: [
5962
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,
5963
6058
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(IconClose, { size: 20, colorToken: "floatingContent" }) })
5964
6059
  ] }),
@@ -5974,12 +6069,12 @@ function ChatPanel({
5974
6069
  ) : null;
5975
6070
  const showMessagesLoading = Boolean(loading) && messages.length === 0 || forking;
5976
6071
  if (showMessagesLoading) {
5977
- return /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_react_native49.View, { style: { flex: 1 }, children: [
5978
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native49.View, { children: header }),
5979
- topBanner ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native49.View, { style: { paddingHorizontal: 16, paddingTop: 8 }, children: topBanner }) : null,
5980
- /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_react_native49.View, { style: { flex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 24, paddingVertical: 12 }, children: [
5981
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native49.ActivityIndicator, {}),
5982
- /* @__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 } }),
5983
6078
  /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(Text, { variant: "bodyMuted", children: forking ? "Creating your copy\u2026" : "Loading messages\u2026" })
5984
6079
  ] })
5985
6080
  ] });
@@ -5991,6 +6086,7 @@ function ChatPanel({
5991
6086
  messages,
5992
6087
  showTypingIndicator,
5993
6088
  topBanner,
6089
+ composerHorizontalPadding: 0,
5994
6090
  listRef,
5995
6091
  onNearBottomChange: setNearBottom,
5996
6092
  overlay: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
@@ -6021,10 +6117,10 @@ function ChatPanel({
6021
6117
 
6022
6118
  // src/components/dialogs/ConfirmMergeRequestDialog.tsx
6023
6119
  var React37 = __toESM(require("react"));
6024
- var import_react_native51 = require("react-native");
6120
+ var import_react_native52 = require("react-native");
6025
6121
 
6026
6122
  // src/components/primitives/Modal.tsx
6027
- var import_react_native50 = require("react-native");
6123
+ var import_react_native51 = require("react-native");
6028
6124
  var import_jsx_runtime52 = require("react/jsx-runtime");
6029
6125
  function Modal({
6030
6126
  visible,
@@ -6035,15 +6131,15 @@ function Modal({
6035
6131
  }) {
6036
6132
  const theme = useTheme();
6037
6133
  return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
6038
- import_react_native50.Modal,
6134
+ import_react_native51.Modal,
6039
6135
  {
6040
6136
  visible,
6041
6137
  transparent: true,
6042
6138
  animationType: "fade",
6043
6139
  onRequestClose,
6044
- 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: [
6045
6141
  /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
6046
- import_react_native50.Pressable,
6142
+ import_react_native51.Pressable,
6047
6143
  {
6048
6144
  accessibilityRole: "button",
6049
6145
  onPress: dismissOnBackdropPress ? onRequestClose : void 0,
@@ -6098,7 +6194,7 @@ function ConfirmMergeRequestDialog({
6098
6194
  backgroundColor: theme.colors.background
6099
6195
  },
6100
6196
  children: [
6101
- /* @__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)(
6102
6198
  Text,
6103
6199
  {
6104
6200
  style: {
@@ -6110,9 +6206,9 @@ function ConfirmMergeRequestDialog({
6110
6206
  children: "Are you sure you want to approve this merge request?"
6111
6207
  }
6112
6208
  ) }),
6113
- /* @__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: [
6114
6210
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6115
- import_react_native51.View,
6211
+ import_react_native52.View,
6116
6212
  {
6117
6213
  style: [
6118
6214
  fullWidthButtonBase,
@@ -6122,7 +6218,7 @@ function ConfirmMergeRequestDialog({
6122
6218
  }
6123
6219
  ],
6124
6220
  children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6125
- import_react_native51.Pressable,
6221
+ import_react_native52.Pressable,
6126
6222
  {
6127
6223
  accessibilityRole: "button",
6128
6224
  accessibilityLabel: "Approve Merge",
@@ -6134,9 +6230,9 @@ function ConfirmMergeRequestDialog({
6134
6230
  )
6135
6231
  }
6136
6232
  ),
6137
- /* @__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 } }),
6138
6234
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6139
- import_react_native51.View,
6235
+ import_react_native52.View,
6140
6236
  {
6141
6237
  style: [
6142
6238
  fullWidthButtonBase,
@@ -6148,7 +6244,7 @@ function ConfirmMergeRequestDialog({
6148
6244
  }
6149
6245
  ],
6150
6246
  children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6151
- import_react_native51.Pressable,
6247
+ import_react_native52.Pressable,
6152
6248
  {
6153
6249
  accessibilityRole: "button",
6154
6250
  accessibilityLabel: isBuilding ? "Preparing\u2026" : "Test edits first",
@@ -6160,9 +6256,9 @@ function ConfirmMergeRequestDialog({
6160
6256
  )
6161
6257
  }
6162
6258
  ),
6163
- /* @__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 } }),
6164
6260
  /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6165
- import_react_native51.View,
6261
+ import_react_native52.View,
6166
6262
  {
6167
6263
  style: [
6168
6264
  fullWidthButtonBase,
@@ -6173,7 +6269,7 @@ function ConfirmMergeRequestDialog({
6173
6269
  }
6174
6270
  ],
6175
6271
  children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6176
- import_react_native51.Pressable,
6272
+ import_react_native52.Pressable,
6177
6273
  {
6178
6274
  accessibilityRole: "button",
6179
6275
  accessibilityLabel: "Cancel",
@@ -6223,6 +6319,92 @@ function ConfirmMergeFlow({
6223
6319
  );
6224
6320
  }
6225
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
+
6226
6408
  // src/studio/ui/StudioOverlay.tsx
6227
6409
  var import_jsx_runtime55 = require("react/jsx-runtime");
6228
6410
  function StudioOverlay({
@@ -6254,33 +6436,40 @@ function StudioOverlay({
6254
6436
  onNavigateHome
6255
6437
  }) {
6256
6438
  const theme = useTheme();
6257
- const { width } = (0, import_react_native52.useWindowDimensions)();
6258
- const [sheetOpen, setSheetOpen] = React38.useState(false);
6259
- const [activePage, setActivePage] = React38.useState("preview");
6260
- const [drawing, setDrawing] = React38.useState(false);
6261
- const [chatAttachments, setChatAttachments] = React38.useState([]);
6262
- const [commentsAppId, setCommentsAppId] = React38.useState(null);
6263
- const [commentsCount, setCommentsCount] = React38.useState(null);
6264
- const [confirmMrId, setConfirmMrId] = React38.useState(null);
6265
- 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(
6266
6455
  () => confirmMrId ? incomingMergeRequests.find((m) => m.id === confirmMrId) ?? null : null,
6267
6456
  [confirmMrId, incomingMergeRequests]
6268
6457
  );
6269
- const handleSheetOpenChange = React38.useCallback((open) => {
6458
+ const handleSheetOpenChange = React39.useCallback((open) => {
6270
6459
  setSheetOpen(open);
6271
- if (!open) import_react_native52.Keyboard.dismiss();
6460
+ if (!open) import_react_native53.Keyboard.dismiss();
6272
6461
  }, []);
6273
- const closeSheet = React38.useCallback(() => {
6462
+ const closeSheet = React39.useCallback(() => {
6274
6463
  handleSheetOpenChange(false);
6275
6464
  }, [handleSheetOpenChange]);
6276
- const openSheet = React38.useCallback(() => setSheetOpen(true), []);
6277
- const goToChat = React38.useCallback(() => {
6465
+ const openSheet = React39.useCallback(() => setSheetOpen(true), []);
6466
+ const goToChat = React39.useCallback(() => {
6278
6467
  setActivePage("chat");
6279
6468
  openSheet();
6280
6469
  }, [openSheet]);
6281
- const backToPreview = React38.useCallback(() => {
6282
- if (import_react_native52.Platform.OS !== "ios") {
6283
- import_react_native52.Keyboard.dismiss();
6470
+ const backToPreview = React39.useCallback(() => {
6471
+ if (import_react_native53.Platform.OS !== "ios") {
6472
+ import_react_native53.Keyboard.dismiss();
6284
6473
  setActivePage("preview");
6285
6474
  return;
6286
6475
  }
@@ -6292,15 +6481,15 @@ function StudioOverlay({
6292
6481
  clearTimeout(t);
6293
6482
  setActivePage("preview");
6294
6483
  };
6295
- const sub = import_react_native52.Keyboard.addListener("keyboardDidHide", finalize);
6484
+ const sub = import_react_native53.Keyboard.addListener("keyboardDidHide", finalize);
6296
6485
  const t = setTimeout(finalize, 350);
6297
- import_react_native52.Keyboard.dismiss();
6486
+ import_react_native53.Keyboard.dismiss();
6298
6487
  }, []);
6299
- const startDraw = React38.useCallback(() => {
6488
+ const startDraw = React39.useCallback(() => {
6300
6489
  setDrawing(true);
6301
6490
  closeSheet();
6302
6491
  }, [closeSheet]);
6303
- const handleDrawCapture = React38.useCallback(
6492
+ const handleDrawCapture = React39.useCallback(
6304
6493
  (dataUrl) => {
6305
6494
  setChatAttachments((prev) => [...prev, dataUrl]);
6306
6495
  setDrawing(false);
@@ -6309,7 +6498,7 @@ function StudioOverlay({
6309
6498
  },
6310
6499
  [openSheet]
6311
6500
  );
6312
- const toggleSheet = React38.useCallback(async () => {
6501
+ const toggleSheet = React39.useCallback(async () => {
6313
6502
  if (!sheetOpen) {
6314
6503
  const shouldExitTest = Boolean(testingMrId) || isTesting;
6315
6504
  if (shouldExitTest) {
@@ -6321,7 +6510,7 @@ function StudioOverlay({
6321
6510
  closeSheet();
6322
6511
  }
6323
6512
  }, [closeSheet, isTesting, onRestoreBase, sheetOpen, testingMrId]);
6324
- const handleTestMr = React38.useCallback(
6513
+ const handleTestMr = React39.useCallback(
6325
6514
  async (mr) => {
6326
6515
  if (!onTestMr) return;
6327
6516
  await onTestMr(mr);
@@ -6365,7 +6554,7 @@ function StudioOverlay({
6365
6554
  chat: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6366
6555
  ChatPanel,
6367
6556
  {
6368
- messages: chatMessages,
6557
+ messages: optimistic.messages,
6369
6558
  showTypingIndicator: chatShowTypingIndicator,
6370
6559
  loading: chatLoading,
6371
6560
  sendDisabled: chatSendDisabled,
@@ -6380,7 +6569,7 @@ function StudioOverlay({
6380
6569
  onClose: closeSheet,
6381
6570
  onNavigateHome,
6382
6571
  onStartDraw: startDraw,
6383
- onSend: onSendChat
6572
+ onSend: optimistic.onSend
6384
6573
  }
6385
6574
  )
6386
6575
  }
@@ -6393,7 +6582,7 @@ function StudioOverlay({
6393
6582
  badgeCount: incomingMergeRequests.length,
6394
6583
  onPress: toggleSheet,
6395
6584
  isLoading: (app == null ? void 0 : app.status) === "editing",
6396
- 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 }) })
6397
6586
  }
6398
6587
  ),
6399
6588
  /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
@@ -6439,16 +6628,16 @@ function ComergeStudio({
6439
6628
  onNavigateHome,
6440
6629
  style
6441
6630
  }) {
6442
- const [activeAppId, setActiveAppId] = React39.useState(appId);
6443
- const [runtimeAppId, setRuntimeAppId] = React39.useState(appId);
6444
- const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React39.useState(null);
6445
- const platform = React39.useMemo(() => import_react_native53.Platform.OS === "ios" ? "ios" : "android", []);
6446
- 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(() => {
6447
6636
  setActiveAppId(appId);
6448
6637
  setRuntimeAppId(appId);
6449
6638
  setPendingRuntimeTargetAppId(null);
6450
6639
  }, [appId]);
6451
- const captureTargetRef = React39.useRef(null);
6640
+ const captureTargetRef = React40.useRef(null);
6452
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)(
6453
6642
  ComergeStudioInner,
6454
6643
  {
@@ -6484,11 +6673,11 @@ function ComergeStudioInner({
6484
6673
  const { app, loading: appLoading } = useApp(activeAppId);
6485
6674
  const { app: runtimeAppFromHook } = useApp(runtimeAppId, { enabled: runtimeAppId !== activeAppId });
6486
6675
  const runtimeApp = runtimeAppId === activeAppId ? app : runtimeAppFromHook;
6487
- const sawEditingOnPendingTargetRef = React39.useRef(false);
6488
- React39.useEffect(() => {
6676
+ const sawEditingOnPendingTargetRef = React40.useRef(false);
6677
+ React40.useEffect(() => {
6489
6678
  sawEditingOnPendingTargetRef.current = false;
6490
6679
  }, [pendingRuntimeTargetAppId]);
6491
- React39.useEffect(() => {
6680
+ React40.useEffect(() => {
6492
6681
  if (!pendingRuntimeTargetAppId) return;
6493
6682
  if (activeAppId !== pendingRuntimeTargetAppId) return;
6494
6683
  if ((app == null ? void 0 : app.status) === "editing") {
@@ -6505,13 +6694,38 @@ function ComergeStudioInner({
6505
6694
  platform,
6506
6695
  canRequestLatest: (runtimeApp == null ? void 0 : runtimeApp.status) === "ready"
6507
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]);
6508
6722
  const threadId = (app == null ? void 0 : app.threadId) ?? "";
6509
6723
  const thread = useThreadMessages(threadId);
6510
6724
  const mergeRequests = useMergeRequests({ appId: activeAppId });
6511
- const hasOpenOutgoingMr = React39.useMemo(() => {
6725
+ const hasOpenOutgoingMr = React40.useMemo(() => {
6512
6726
  return mergeRequests.lists.outgoing.some((mr) => mr.status === "open");
6513
6727
  }, [mergeRequests.lists.outgoing]);
6514
- const incomingReviewMrs = React39.useMemo(() => {
6728
+ const incomingReviewMrs = React40.useMemo(() => {
6515
6729
  if (!userId) return mergeRequests.lists.incoming;
6516
6730
  return mergeRequests.lists.incoming.filter((mr) => mr.createdBy !== userId);
6517
6731
  }, [mergeRequests.lists.incoming, userId]);
@@ -6533,17 +6747,25 @@ function ComergeStudioInner({
6533
6747
  uploadAttachments: uploader.uploadBase64Images
6534
6748
  });
6535
6749
  const chatSendDisabled = hasNoOutcomeAfterLastHuman(thread.raw);
6536
- const [processingMrId, setProcessingMrId] = React39.useState(null);
6537
- const [testingMrId, setTestingMrId] = React39.useState(null);
6538
- const chatShowTypingIndicator = React39.useMemo(() => {
6750
+ const [processingMrId, setProcessingMrId] = React40.useState(null);
6751
+ const [testingMrId, setTestingMrId] = React40.useState(null);
6752
+ const chatShowTypingIndicator = React40.useMemo(() => {
6539
6753
  var _a;
6540
6754
  if (!thread.raw || thread.raw.length === 0) return false;
6541
6755
  const last = thread.raw[thread.raw.length - 1];
6542
6756
  const payloadType = typeof ((_a = last.payload) == null ? void 0 : _a.type) === "string" ? String(last.payload.type) : void 0;
6543
6757
  return payloadType !== "outcome";
6544
6758
  }, [thread.raw]);
6545
- 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: [
6546
- /* @__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
+ ),
6547
6769
  /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
6548
6770
  StudioOverlay,
6549
6771
  {