@comergehq/studio 0.1.9 → 0.1.11

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 React42 = __toESM(require("react"));
40
+ var import_react_native55 = 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,25 +1812,76 @@ 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 React41 = __toESM(require("react"));
1816
+ var import_react_native54 = require("react-native");
1716
1817
 
1717
1818
  // src/components/studio-sheet/StudioBottomSheet.tsx
1718
- var React9 = __toESM(require("react"));
1719
- var import_react_native7 = require("react-native");
1819
+ var React11 = __toESM(require("react"));
1820
+ var import_react_native9 = 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_native8 = require("react-native");
1826
+ var import_liquid_glass2 = require("@callstack/liquid-glass");
1827
+
1828
+ // src/components/utils/ResettableLiquidGlassView.tsx
1829
+ var React10 = __toESM(require("react"));
1725
1830
  var import_liquid_glass = require("@callstack/liquid-glass");
1831
+
1832
+ // src/components/utils/liquidGlassReset.tsx
1833
+ var React9 = __toESM(require("react"));
1834
+ var import_react_native7 = require("react-native");
1726
1835
  var import_jsx_runtime4 = require("react/jsx-runtime");
1836
+ var LiquidGlassResetContext = React9.createContext(0);
1837
+ function LiquidGlassResetProvider({
1838
+ children,
1839
+ resetTriggers = []
1840
+ }) {
1841
+ const [token, setToken] = React9.useState(0);
1842
+ React9.useEffect(() => {
1843
+ if (import_react_native7.Platform.OS !== "ios") return;
1844
+ const onChange = (state) => {
1845
+ if (state === "active") setToken((t) => t + 1);
1846
+ };
1847
+ const sub = import_react_native7.AppState.addEventListener("change", onChange);
1848
+ return () => sub.remove();
1849
+ }, []);
1850
+ React9.useEffect(() => {
1851
+ setToken((t) => t + 1);
1852
+ }, resetTriggers);
1853
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LiquidGlassResetContext.Provider, { value: token, children });
1854
+ }
1855
+ function useLiquidGlassResetToken() {
1856
+ return React9.useContext(LiquidGlassResetContext);
1857
+ }
1858
+
1859
+ // src/components/utils/ResettableLiquidGlassView.tsx
1860
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1861
+ function ResettableLiquidGlassView({ children, ...props }) {
1862
+ const token = useLiquidGlassResetToken();
1863
+ const [layoutBootKey, setLayoutBootKey] = React10.useState(0);
1864
+ const sawNonZeroLayoutRef = React10.useRef(false);
1865
+ const onLayout = (e) => {
1866
+ var _a;
1867
+ (_a = props.onLayout) == null ? void 0 : _a.call(props, e);
1868
+ const { width, height } = e.nativeEvent.layout;
1869
+ if (width > 0 && height > 0 && !sawNonZeroLayoutRef.current) {
1870
+ sawNonZeroLayoutRef.current = true;
1871
+ setLayoutBootKey((k) => k + 1);
1872
+ }
1873
+ };
1874
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_liquid_glass.LiquidGlassView, { ...props, onLayout, children }, `liquid-glass-${token}-${layoutBootKey}`);
1875
+ }
1876
+
1877
+ // src/components/studio-sheet/StudioSheetBackground.tsx
1878
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1727
1879
  function StudioSheetBackground({
1728
1880
  style,
1729
1881
  renderBackground
1730
1882
  }) {
1731
1883
  const theme = useTheme();
1732
- const radius = import_react_native6.Platform.OS === "ios" ? 39 : 16;
1884
+ const radius = import_react_native8.Platform.OS === "ios" ? 39 : 16;
1733
1885
  const fallbackBgColor = theme.scheme === "dark" ? "rgba(11, 8, 15, 0.85)" : "rgba(255, 255, 255, 0.85)";
1734
1886
  const secondaryBgBaseColor = theme.scheme === "dark" ? "rgb(24, 24, 27)" : "rgb(173, 173, 173)";
1735
1887
  const containerStyle = {
@@ -1739,18 +1891,18 @@ function StudioSheetBackground({
1739
1891
  overflow: "hidden"
1740
1892
  };
1741
1893
  if (renderBackground) {
1742
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: renderBackground({ style: containerStyle }) });
1894
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: renderBackground({ style: containerStyle }) });
1743
1895
  }
1744
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1745
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1746
- import_liquid_glass.LiquidGlassView,
1896
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(import_jsx_runtime6.Fragment, { children: [
1897
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1898
+ ResettableLiquidGlassView,
1747
1899
  {
1748
- style: [containerStyle, !import_liquid_glass.isLiquidGlassSupported && { backgroundColor: fallbackBgColor }],
1900
+ style: [containerStyle, !import_liquid_glass2.isLiquidGlassSupported && { backgroundColor: fallbackBgColor }],
1749
1901
  effect: "regular"
1750
1902
  }
1751
1903
  ),
1752
- import_liquid_glass.isLiquidGlassSupported && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1753
- import_react_native6.View,
1904
+ import_liquid_glass2.isLiquidGlassSupported && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1905
+ import_react_native8.View,
1754
1906
  {
1755
1907
  style: [
1756
1908
  containerStyle,
@@ -1771,11 +1923,11 @@ function StudioSheetBackground({
1771
1923
  }
1772
1924
 
1773
1925
  // src/components/studio-sheet/StudioBottomSheet.tsx
1774
- var import_jsx_runtime5 = require("react/jsx-runtime");
1926
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1775
1927
  function StudioBottomSheet({
1776
1928
  open,
1777
1929
  onOpenChange,
1778
- snapPoints = ["80%", "100%"],
1930
+ snapPoints = ["100%"],
1779
1931
  sheetRef,
1780
1932
  background,
1781
1933
  children,
@@ -1783,16 +1935,16 @@ function StudioBottomSheet({
1783
1935
  }) {
1784
1936
  const theme = useTheme();
1785
1937
  const insets = (0, import_react_native_safe_area_context.useSafeAreaInsets)();
1786
- const internalSheetRef = React9.useRef(null);
1938
+ const internalSheetRef = React11.useRef(null);
1787
1939
  const resolvedSheetRef = sheetRef ?? internalSheetRef;
1788
- const currentIndexRef = React9.useRef(open ? snapPoints.length - 1 : -1);
1789
- const lastAppStateRef = React9.useRef(import_react_native7.AppState.currentState);
1790
- React9.useEffect(() => {
1791
- const sub = import_react_native7.AppState.addEventListener("change", (state) => {
1940
+ const currentIndexRef = React11.useRef(open ? snapPoints.length - 1 : -1);
1941
+ const lastAppStateRef = React11.useRef(import_react_native9.AppState.currentState);
1942
+ React11.useEffect(() => {
1943
+ const sub = import_react_native9.AppState.addEventListener("change", (state) => {
1792
1944
  const prev = lastAppStateRef.current;
1793
1945
  lastAppStateRef.current = state;
1794
1946
  if (state === "background" || state === "inactive") {
1795
- import_react_native7.Keyboard.dismiss();
1947
+ import_react_native9.Keyboard.dismiss();
1796
1948
  return;
1797
1949
  }
1798
1950
  if (state !== "active") return;
@@ -1800,14 +1952,14 @@ function StudioBottomSheet({
1800
1952
  if (!sheet) return;
1801
1953
  const idx = currentIndexRef.current;
1802
1954
  if (open && idx >= 0) {
1803
- import_react_native7.Keyboard.dismiss();
1955
+ import_react_native9.Keyboard.dismiss();
1804
1956
  requestAnimationFrame(() => sheet.snapToIndex(idx));
1805
1957
  setTimeout(() => sheet.snapToIndex(idx), 120);
1806
1958
  }
1807
1959
  });
1808
1960
  return () => sub.remove();
1809
1961
  }, [open, resolvedSheetRef]);
1810
- React9.useEffect(() => {
1962
+ React11.useEffect(() => {
1811
1963
  const sheet = resolvedSheetRef.current;
1812
1964
  if (!sheet) return;
1813
1965
  if (open) {
@@ -1816,42 +1968,42 @@ function StudioBottomSheet({
1816
1968
  sheet.close();
1817
1969
  }
1818
1970
  }, [open, resolvedSheetRef, snapPoints.length]);
1819
- const handleChange = React9.useCallback(
1971
+ const handleChange = React11.useCallback(
1820
1972
  (index) => {
1821
1973
  currentIndexRef.current = index;
1822
1974
  onOpenChange == null ? void 0 : onOpenChange(index >= 0);
1823
1975
  },
1824
1976
  [onOpenChange]
1825
1977
  );
1826
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1978
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1827
1979
  import_bottom_sheet.default,
1828
1980
  {
1829
1981
  ref: resolvedSheetRef,
1830
1982
  index: open ? snapPoints.length - 1 : -1,
1831
1983
  snapPoints,
1984
+ enableDynamicSizing: false,
1832
1985
  enablePanDownToClose: true,
1833
- keyboardBehavior: "interactive",
1834
- keyboardBlurBehavior: "restore",
1986
+ enableContentPanningGesture: false,
1835
1987
  android_keyboardInputMode: "adjustResize",
1836
- backgroundComponent: (props) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(StudioSheetBackground, { ...props, renderBackground: background == null ? void 0 : background.renderBackground }),
1988
+ backgroundComponent: (props) => /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(StudioSheetBackground, { ...props, renderBackground: background == null ? void 0 : background.renderBackground }),
1837
1989
  topInset: insets.top,
1838
1990
  bottomInset: 0,
1839
1991
  handleIndicatorStyle: { backgroundColor: theme.colors.handleIndicator },
1840
1992
  onChange: handleChange,
1841
1993
  ...bottomSheetProps,
1842
- children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_native7.View, { style: { flex: 1, overflow: "hidden" }, children })
1994
+ children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native9.View, { style: { flex: 1, overflow: "hidden" }, children })
1843
1995
  }
1844
1996
  );
1845
1997
  }
1846
1998
 
1847
1999
  // src/components/studio-sheet/StudioSheetPager.tsx
1848
- var React10 = __toESM(require("react"));
1849
- var import_react_native8 = require("react-native");
1850
- var import_jsx_runtime6 = require("react/jsx-runtime");
2000
+ var React12 = __toESM(require("react"));
2001
+ var import_react_native10 = require("react-native");
2002
+ var import_jsx_runtime8 = require("react/jsx-runtime");
1851
2003
  function StudioSheetPager({ activePage, width, preview, chat, style }) {
1852
- const anim = React10.useRef(new import_react_native8.Animated.Value(activePage === "chat" ? 1 : 0)).current;
1853
- React10.useEffect(() => {
1854
- import_react_native8.Animated.spring(anim, {
2004
+ const anim = React12.useRef(new import_react_native10.Animated.Value(activePage === "chat" ? 1 : 0)).current;
2005
+ React12.useEffect(() => {
2006
+ import_react_native10.Animated.spring(anim, {
1855
2007
  toValue: activePage === "chat" ? 1 : 0,
1856
2008
  useNativeDriver: true,
1857
2009
  tension: 65,
@@ -1860,9 +2012,9 @@ function StudioSheetPager({ activePage, width, preview, chat, style }) {
1860
2012
  }, [activePage, anim]);
1861
2013
  const previewTranslateX = anim.interpolate({ inputRange: [0, 1], outputRange: [0, -width] });
1862
2014
  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: [
1864
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1865
- import_react_native8.Animated.View,
2015
+ return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(import_react_native10.Animated.View, { style: [{ flex: 1 }, style], children: [
2016
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2017
+ import_react_native10.Animated.View,
1866
2018
  {
1867
2019
  style: [
1868
2020
  {
@@ -1878,8 +2030,8 @@ function StudioSheetPager({ activePage, width, preview, chat, style }) {
1878
2030
  children: preview
1879
2031
  }
1880
2032
  ),
1881
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1882
- import_react_native8.Animated.View,
2033
+ /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
2034
+ import_react_native10.Animated.View,
1883
2035
  {
1884
2036
  style: [
1885
2037
  {
@@ -1900,10 +2052,10 @@ function StudioSheetPager({ activePage, width, preview, chat, style }) {
1900
2052
 
1901
2053
  // src/components/floating-draggable-button/FloatingDraggableButton.tsx
1902
2054
  var import_react = require("react");
1903
- var import_react_native9 = require("react-native");
2055
+ var import_react_native11 = require("react-native");
1904
2056
  var Haptics = __toESM(require("expo-haptics"));
1905
2057
  var import_react_native_reanimated = __toESM(require("react-native-reanimated"));
1906
- var import_liquid_glass2 = require("@callstack/liquid-glass");
2058
+ var import_liquid_glass3 = require("@callstack/liquid-glass");
1907
2059
 
1908
2060
  // src/components/floating-draggable-button/constants.ts
1909
2061
  var DEFAULT_SIZE = 48;
@@ -1914,11 +2066,10 @@ var DEFAULT_OFFSET = {
1914
2066
  };
1915
2067
  var ENTER_SCALE_FROM = 0.3;
1916
2068
  var ENTER_ROTATION_FROM_DEG = -180;
1917
- var HIDDEN_OPACITY = 0.3;
1918
2069
  var PULSE_DURATION_MS = 900;
1919
2070
 
1920
2071
  // src/components/floating-draggable-button/FloatingDraggableButton.tsx
1921
- var import_jsx_runtime7 = require("react/jsx-runtime");
2072
+ var import_jsx_runtime9 = require("react/jsx-runtime");
1922
2073
  var HIDDEN_OFFSET_X = 20;
1923
2074
  var SPRING_POSITION = { damping: 12, stiffness: 100, mass: 0.8 };
1924
2075
  var SPRING_SCALE_IN = { damping: 10, stiffness: 200 };
@@ -1926,8 +2077,6 @@ var SPRING_SCALE_OUT = { damping: 12, stiffness: 150 };
1926
2077
  var SPRING_ROTATION_IN = { damping: 15, stiffness: 80 };
1927
2078
  var SPRING_ROTATION_GRAB = { damping: 20 };
1928
2079
  var SPRING_SCALE_GRAB = { damping: 15, stiffness: 200 };
1929
- var TIMING_OPACITY_IN = { duration: 300, easing: import_react_native_reanimated.Easing.out(import_react_native_reanimated.Easing.ease) };
1930
- var TIMING_OPACITY_OUT = { duration: 250, easing: import_react_native_reanimated.Easing.in(import_react_native_reanimated.Easing.ease) };
1931
2080
  function clamp(value, min, max) {
1932
2081
  "worklet";
1933
2082
  return Math.max(min, Math.min(max, value));
@@ -1959,7 +2108,7 @@ function FloatingDraggableButton({
1959
2108
  backgroundColor
1960
2109
  }) {
1961
2110
  const theme = useTheme();
1962
- const { width, height } = (0, import_react_native9.useWindowDimensions)();
2111
+ const { width, height } = (0, import_react_native11.useWindowDimensions)();
1963
2112
  const isDanger = variant === "danger";
1964
2113
  const onPressRef = (0, import_react.useRef)(onPress);
1965
2114
  (0, import_react.useEffect)(() => {
@@ -1974,30 +2123,20 @@ function FloatingDraggableButton({
1974
2123
  const translateY = (0, import_react_native_reanimated.useSharedValue)(getHiddenTranslateY(height));
1975
2124
  const scale = (0, import_react_native_reanimated.useSharedValue)(ENTER_SCALE_FROM);
1976
2125
  const rotation = (0, import_react_native_reanimated.useSharedValue)(ENTER_ROTATION_FROM_DEG);
1977
- const opacity = (0, import_react_native_reanimated.useSharedValue)(1);
1978
2126
  const borderPulse = (0, import_react_native_reanimated.useSharedValue)(0);
1979
2127
  const startPos = (0, import_react.useRef)({ x: 0, y: 0 });
1980
2128
  const isAnimatingOut = (0, import_react.useRef)(false);
1981
2129
  const animateToHidden = (0, import_react.useCallback)(
1982
2130
  (options) => {
1983
- translateX.value = (0, import_react_native_reanimated.withSpring)(getHiddenTranslateX(size), SPRING_POSITION);
2131
+ const finish = options == null ? void 0 : options.onFinish;
2132
+ translateX.value = (0, import_react_native_reanimated.withSpring)(getHiddenTranslateX(size), SPRING_POSITION, (finished) => {
2133
+ if (finished && finish) (0, import_react_native_reanimated.runOnJS)(finish)();
2134
+ });
1984
2135
  translateY.value = (0, import_react_native_reanimated.withSpring)(getHiddenTranslateY(height), SPRING_POSITION);
1985
2136
  scale.value = (0, import_react_native_reanimated.withSpring)(ENTER_SCALE_FROM, SPRING_SCALE_IN);
1986
2137
  rotation.value = (0, import_react_native_reanimated.withSpring)(ENTER_ROTATION_FROM_DEG, SPRING_ROTATION_IN);
1987
- const finish = options == null ? void 0 : options.onFinish;
1988
- if (!finish) {
1989
- opacity.value = (0, import_react_native_reanimated.withTiming)(HIDDEN_OPACITY, TIMING_OPACITY_OUT);
1990
- return;
1991
- }
1992
- opacity.value = (0, import_react_native_reanimated.withTiming)(
1993
- HIDDEN_OPACITY,
1994
- TIMING_OPACITY_OUT,
1995
- (finished) => {
1996
- if (finished) (0, import_react_native_reanimated.runOnJS)(finish)();
1997
- }
1998
- );
1999
2138
  },
2000
- [height, opacity, rotation, scale, size, translateX, translateY]
2139
+ [height, rotation, scale, size, translateX, translateY]
2001
2140
  );
2002
2141
  const animateOut = (0, import_react.useCallback)(() => {
2003
2142
  if (isAnimatingOut.current) return;
@@ -2037,8 +2176,7 @@ function FloatingDraggableButton({
2037
2176
  (0, import_react_native_reanimated.withSpring)(1, SPRING_SCALE_OUT)
2038
2177
  );
2039
2178
  rotation.value = (0, import_react_native_reanimated.withSpring)(0, SPRING_ROTATION_IN);
2040
- opacity.value = (0, import_react_native_reanimated.withTiming)(1, TIMING_OPACITY_IN);
2041
- }, [height, offset.bottom, offset.left, opacity, rotation, scale, size, translateX, translateY]);
2179
+ }, [height, offset.bottom, offset.left, rotation, scale, size, translateX, translateY]);
2042
2180
  (0, import_react.useEffect)(() => {
2043
2181
  const timer = setTimeout(() => {
2044
2182
  if (visible) {
@@ -2062,7 +2200,7 @@ function FloatingDraggableButton({
2062
2200
  }
2063
2201
  }, [forceShowTrigger, visible, animateIn]);
2064
2202
  const panResponder = (0, import_react.useRef)(
2065
- import_react_native9.PanResponder.create({
2203
+ import_react_native11.PanResponder.create({
2066
2204
  onStartShouldSetPanResponder: () => true,
2067
2205
  onMoveShouldSetPanResponder: () => true,
2068
2206
  onPanResponderGrant: () => {
@@ -2094,8 +2232,7 @@ function FloatingDraggableButton({
2094
2232
  { translateY: translateY.value },
2095
2233
  { scale: scale.value },
2096
2234
  { rotate: `${rotation.value}deg` }
2097
- ],
2098
- opacity: opacity.value
2235
+ ]
2099
2236
  }));
2100
2237
  const borderAnimatedStyle = (0, import_react_native_reanimated.useAnimatedStyle)(() => {
2101
2238
  const borderColor = (0, import_react_native_reanimated.interpolateColor)(
@@ -2109,7 +2246,7 @@ function FloatingDraggableButton({
2109
2246
  borderRadius: size / 2
2110
2247
  };
2111
2248
  });
2112
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
2249
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
2113
2250
  import_react_native_reanimated.default.View,
2114
2251
  {
2115
2252
  ...panResponder.panHandlers,
@@ -2118,31 +2255,31 @@ function FloatingDraggableButton({
2118
2255
  accessibilityRole: "button",
2119
2256
  accessibilityLabel: ariaLabel,
2120
2257
  children: [
2121
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native_reanimated.default.View, { style: [{ width: size, height: size, borderRadius: size / 2 }, borderAnimatedStyle], children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2122
- import_liquid_glass2.LiquidGlassView,
2258
+ /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native_reanimated.default.View, { style: [{ width: size, height: size, borderRadius: size / 2 }, borderAnimatedStyle], children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2259
+ ResettableLiquidGlassView,
2123
2260
  {
2124
- style: [{ flex: 1, borderRadius: size / 2 }, !import_liquid_glass2.isLiquidGlassSupported && { backgroundColor: fallbackBgColor }],
2261
+ style: [{ flex: 1, borderRadius: size / 2 }, !import_liquid_glass3.isLiquidGlassSupported && { backgroundColor: fallbackBgColor }],
2125
2262
  interactive: true,
2126
2263
  effect: "clear",
2127
- children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
2128
- import_react_native9.Pressable,
2264
+ children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2265
+ import_react_native11.Pressable,
2129
2266
  {
2130
2267
  onPress: () => {
2131
2268
  if (!disabled) animateOut();
2132
2269
  },
2133
2270
  style: styles.buttonInner,
2134
2271
  android_ripple: { color: "rgba(255, 255, 255, 0.3)", borderless: true },
2135
- children: children ?? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(import_react_native9.View, {})
2272
+ children: children ?? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native11.View, {})
2136
2273
  }
2137
2274
  )
2138
2275
  }
2139
2276
  ) }),
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 }) })
2277
+ badgeCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native11.View, { style: [styles.badge, { backgroundColor: theme.colors.danger }], children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react_native11.Text, { style: [styles.badgeText, { color: theme.colors.onDanger }], children: badgeCount > 99 ? "99+" : badgeCount }) })
2141
2278
  ]
2142
2279
  }
2143
2280
  );
2144
2281
  }
2145
- var styles = import_react_native9.StyleSheet.create({
2282
+ var styles = import_react_native11.StyleSheet.create({
2146
2283
  floatingButton: {
2147
2284
  position: "absolute",
2148
2285
  justifyContent: "center",
@@ -2178,8 +2315,8 @@ var styles = import_react_native9.StyleSheet.create({
2178
2315
  });
2179
2316
 
2180
2317
  // src/components/overlays/EdgeGlowFrame.tsx
2181
- var React11 = __toESM(require("react"));
2182
- var import_react_native10 = require("react-native");
2318
+ var React13 = __toESM(require("react"));
2319
+ var import_react_native12 = require("react-native");
2183
2320
  var import_expo_linear_gradient = require("expo-linear-gradient");
2184
2321
 
2185
2322
  // src/components/utils/color.ts
@@ -2198,7 +2335,7 @@ function withAlpha(color, alpha) {
2198
2335
  }
2199
2336
 
2200
2337
  // src/components/overlays/EdgeGlowFrame.tsx
2201
- var import_jsx_runtime8 = require("react/jsx-runtime");
2338
+ var import_jsx_runtime10 = require("react/jsx-runtime");
2202
2339
  function baseColor(role, theme) {
2203
2340
  switch (role) {
2204
2341
  case "danger":
@@ -2221,9 +2358,9 @@ function EdgeGlowFrame({
2221
2358
  }) {
2222
2359
  const theme = useTheme();
2223
2360
  const alpha = Math.max(0, Math.min(1, intensity));
2224
- const anim = React11.useRef(new import_react_native10.Animated.Value(visible ? 1 : 0)).current;
2225
- React11.useEffect(() => {
2226
- import_react_native10.Animated.timing(anim, {
2361
+ const anim = React13.useRef(new import_react_native12.Animated.Value(visible ? 1 : 0)).current;
2362
+ React13.useEffect(() => {
2363
+ import_react_native12.Animated.timing(anim, {
2227
2364
  toValue: visible ? 1 : 0,
2228
2365
  duration: 300,
2229
2366
  useNativeDriver: true
@@ -2232,8 +2369,8 @@ function EdgeGlowFrame({
2232
2369
  const c = baseColor(role, theme);
2233
2370
  const strong = withAlpha(c, 0.6 * alpha);
2234
2371
  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)(
2372
+ return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_react_native12.Animated.View, { pointerEvents: "none", style: [{ position: "absolute", inset: 0, opacity: anim }, style], children: [
2373
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: { position: "absolute", top: 0, left: 0, right: 0, height: thickness }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2237
2374
  import_expo_linear_gradient.LinearGradient,
2238
2375
  {
2239
2376
  colors: [strong, soft, "transparent"],
@@ -2242,7 +2379,7 @@ function EdgeGlowFrame({
2242
2379
  style: { width: "100%", height: "100%" }
2243
2380
  }
2244
2381
  ) }),
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)(
2382
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: { position: "absolute", bottom: 0, left: 0, right: 0, height: thickness }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2246
2383
  import_expo_linear_gradient.LinearGradient,
2247
2384
  {
2248
2385
  colors: ["transparent", soft, strong],
@@ -2251,7 +2388,7 @@ function EdgeGlowFrame({
2251
2388
  style: { width: "100%", height: "100%" }
2252
2389
  }
2253
2390
  ) }),
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)(
2391
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: { position: "absolute", top: 0, bottom: 0, left: 0, width: thickness }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2255
2392
  import_expo_linear_gradient.LinearGradient,
2256
2393
  {
2257
2394
  colors: [strong, soft, "transparent"],
@@ -2260,7 +2397,7 @@ function EdgeGlowFrame({
2260
2397
  style: { width: "100%", height: "100%" }
2261
2398
  }
2262
2399
  ) }),
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)(
2400
+ /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.View, { style: { position: "absolute", top: 0, bottom: 0, right: 0, width: thickness }, children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
2264
2401
  import_expo_linear_gradient.LinearGradient,
2265
2402
  {
2266
2403
  colors: ["transparent", soft, strong],
@@ -2273,13 +2410,13 @@ function EdgeGlowFrame({
2273
2410
  }
2274
2411
 
2275
2412
  // src/components/draw/DrawModeOverlay.tsx
2276
- var React14 = __toESM(require("react"));
2277
- var import_react_native14 = require("react-native");
2413
+ var React16 = __toESM(require("react"));
2414
+ var import_react_native16 = require("react-native");
2278
2415
  var import_react_native_view_shot = require("react-native-view-shot");
2279
2416
 
2280
2417
  // src/components/draw/DrawSurface.tsx
2281
- var React12 = __toESM(require("react"));
2282
- var import_react_native11 = require("react-native");
2418
+ var React14 = __toESM(require("react"));
2419
+ var import_react_native13 = require("react-native");
2283
2420
  var import_react_native_svg = __toESM(require("react-native-svg"));
2284
2421
 
2285
2422
  // src/components/draw/strokes.ts
@@ -2301,7 +2438,7 @@ function pointsToSmoothPath(points) {
2301
2438
  }
2302
2439
 
2303
2440
  // src/components/draw/DrawSurface.tsx
2304
- var import_jsx_runtime9 = require("react/jsx-runtime");
2441
+ var import_jsx_runtime11 = require("react/jsx-runtime");
2305
2442
  function DrawSurface({
2306
2443
  color,
2307
2444
  strokeWidth,
@@ -2310,25 +2447,25 @@ function DrawSurface({
2310
2447
  style,
2311
2448
  minDistance = 1
2312
2449
  }) {
2313
- const [renderTick, setRenderTick] = React12.useState(0);
2314
- const currentPointsRef = React12.useRef([]);
2315
- const rafRef = React12.useRef(null);
2316
- const triggerRender = React12.useCallback(() => {
2450
+ const [renderTick, setRenderTick] = React14.useState(0);
2451
+ const currentPointsRef = React14.useRef([]);
2452
+ const rafRef = React14.useRef(null);
2453
+ const triggerRender = React14.useCallback(() => {
2317
2454
  if (rafRef.current !== null) return;
2318
2455
  rafRef.current = requestAnimationFrame(() => {
2319
2456
  rafRef.current = null;
2320
2457
  setRenderTick((n) => n + 1);
2321
2458
  });
2322
2459
  }, []);
2323
- React12.useEffect(() => () => {
2460
+ React14.useEffect(() => () => {
2324
2461
  if (rafRef.current !== null) cancelAnimationFrame(rafRef.current);
2325
2462
  }, []);
2326
- const onStart = React12.useCallback((e) => {
2463
+ const onStart = React14.useCallback((e) => {
2327
2464
  const { locationX, locationY } = e.nativeEvent;
2328
2465
  currentPointsRef.current = [{ x: locationX, y: locationY }];
2329
2466
  triggerRender();
2330
2467
  }, [triggerRender]);
2331
- const onMove = React12.useCallback((e, _g) => {
2468
+ const onMove = React14.useCallback((e, _g) => {
2332
2469
  const { locationX, locationY } = e.nativeEvent;
2333
2470
  const pts = currentPointsRef.current;
2334
2471
  if (pts.length > 0) {
@@ -2341,7 +2478,7 @@ function DrawSurface({
2341
2478
  currentPointsRef.current = [...pts, { x: locationX, y: locationY }];
2342
2479
  triggerRender();
2343
2480
  }, [minDistance, triggerRender]);
2344
- const onEnd = React12.useCallback(() => {
2481
+ const onEnd = React14.useCallback(() => {
2345
2482
  const points = currentPointsRef.current;
2346
2483
  if (points.length > 0) {
2347
2484
  onAddStroke({ points, color, width: strokeWidth });
@@ -2349,8 +2486,8 @@ function DrawSurface({
2349
2486
  currentPointsRef.current = [];
2350
2487
  triggerRender();
2351
2488
  }, [color, onAddStroke, strokeWidth, triggerRender]);
2352
- const panResponder = React12.useMemo(
2353
- () => import_react_native11.PanResponder.create({
2489
+ const panResponder = React14.useMemo(
2490
+ () => import_react_native13.PanResponder.create({
2354
2491
  onStartShouldSetPanResponder: () => true,
2355
2492
  onMoveShouldSetPanResponder: () => true,
2356
2493
  onPanResponderGrant: onStart,
@@ -2362,11 +2499,11 @@ function DrawSurface({
2362
2499
  );
2363
2500
  const currentPath = pointsToSmoothPath(currentPointsRef.current);
2364
2501
  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: [
2502
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react_native13.View, { style: [import_react_native13.StyleSheet.absoluteFill, styles2.container, style], ...panResponder.panHandlers, children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native_svg.default, { style: import_react_native13.StyleSheet.absoluteFill, width: "100%", height: "100%", children: [
2366
2503
  strokes.map((s, idx) => {
2367
2504
  const d = pointsToSmoothPath(s.points);
2368
2505
  if (!d) return null;
2369
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2506
+ return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2370
2507
  import_react_native_svg.Path,
2371
2508
  {
2372
2509
  d,
@@ -2379,7 +2516,7 @@ function DrawSurface({
2379
2516
  idx
2380
2517
  );
2381
2518
  }),
2382
- currentPath ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
2519
+ currentPath ? /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2383
2520
  import_react_native_svg.Path,
2384
2521
  {
2385
2522
  d: currentPath,
@@ -2392,15 +2529,15 @@ function DrawSurface({
2392
2529
  ) : null
2393
2530
  ] }) });
2394
2531
  }
2395
- var styles2 = import_react_native11.StyleSheet.create({
2532
+ var styles2 = import_react_native13.StyleSheet.create({
2396
2533
  container: {
2397
2534
  zIndex: 5
2398
2535
  }
2399
2536
  });
2400
2537
 
2401
2538
  // src/components/draw/DrawToolbar.tsx
2402
- var React13 = __toESM(require("react"));
2403
- var import_react_native13 = require("react-native");
2539
+ var React15 = __toESM(require("react"));
2540
+ var import_react_native15 = require("react-native");
2404
2541
  var import_react_native_safe_area_context2 = require("react-native-safe-area-context");
2405
2542
  var import_lucide_react_native = require("lucide-react-native");
2406
2543
 
@@ -2419,8 +2556,8 @@ async function impact(style) {
2419
2556
  }
2420
2557
 
2421
2558
  // src/components/draw/DrawColorPicker.tsx
2422
- var import_react_native12 = require("react-native");
2423
- var import_jsx_runtime10 = require("react/jsx-runtime");
2559
+ var import_react_native14 = require("react-native");
2560
+ var import_jsx_runtime12 = require("react/jsx-runtime");
2424
2561
  function DrawColorPicker({
2425
2562
  colors,
2426
2563
  selected,
@@ -2454,10 +2591,10 @@ function DrawColorPicker({
2454
2591
  return { ...base, ...selectedStyle, ...whiteStyle };
2455
2592
  };
2456
2593
  if (!expanded) {
2457
- return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(import_react_native12.Pressable, { onPress: onToggle, style: [swatchStyle(selected, true), style] });
2594
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native14.Pressable, { onPress: onToggle, style: [swatchStyle(selected, true), style] });
2458
2595
  }
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,
2596
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(import_react_native14.View, { style: [{ flexDirection: "row", alignItems: "center", gap: 8 }, style], children: colors.map((c, idx) => /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2597
+ import_react_native14.Pressable,
2461
2598
  {
2462
2599
  onPress: () => {
2463
2600
  onSelect(c);
@@ -2470,7 +2607,7 @@ function DrawColorPicker({
2470
2607
  }
2471
2608
 
2472
2609
  // src/components/draw/DrawToolbar.tsx
2473
- var import_jsx_runtime11 = require("react/jsx-runtime");
2610
+ var import_jsx_runtime13 = require("react/jsx-runtime");
2474
2611
  function DrawToolbar({
2475
2612
  colors,
2476
2613
  selectedColor,
@@ -2488,14 +2625,14 @@ function DrawToolbar({
2488
2625
  style
2489
2626
  }) {
2490
2627
  const insets = (0, import_react_native_safe_area_context2.useSafeAreaInsets)();
2491
- const { width: screenWidth, height: screenHeight } = (0, import_react_native13.useWindowDimensions)();
2492
- const [expanded, setExpanded] = React13.useState(false);
2493
- const pos = React13.useRef(new import_react_native13.Animated.ValueXY({ x: screenWidth / 2 - 110, y: -140 })).current;
2494
- const start = React13.useRef({ x: 0, y: 0 });
2495
- const currentPos = React13.useRef({ x: 0, y: 0 });
2496
- React13.useEffect(() => {
2628
+ const { width: screenWidth, height: screenHeight } = (0, import_react_native15.useWindowDimensions)();
2629
+ const [expanded, setExpanded] = React15.useState(false);
2630
+ const pos = React15.useRef(new import_react_native15.Animated.ValueXY({ x: screenWidth / 2 - 110, y: -140 })).current;
2631
+ const start = React15.useRef({ x: 0, y: 0 });
2632
+ const currentPos = React15.useRef({ x: 0, y: 0 });
2633
+ React15.useEffect(() => {
2497
2634
  if (hidden) return;
2498
- import_react_native13.Animated.spring(pos.y, {
2635
+ import_react_native15.Animated.spring(pos.y, {
2499
2636
  toValue: insets.top + 60,
2500
2637
  useNativeDriver: true,
2501
2638
  damping: 12,
@@ -2503,7 +2640,7 @@ function DrawToolbar({
2503
2640
  mass: 0.8
2504
2641
  }).start();
2505
2642
  }, [hidden, insets.top, pos.y]);
2506
- React13.useEffect(() => {
2643
+ React15.useEffect(() => {
2507
2644
  const id = pos.addListener((v) => {
2508
2645
  currentPos.current = { x: v.x ?? 0, y: v.y ?? 0 };
2509
2646
  });
@@ -2511,7 +2648,7 @@ function DrawToolbar({
2511
2648
  pos.removeListener(id);
2512
2649
  };
2513
2650
  }, [pos]);
2514
- const clamp2 = React13.useCallback(
2651
+ const clamp2 = React15.useCallback(
2515
2652
  (x, y) => {
2516
2653
  const minX = 10;
2517
2654
  const maxX = Math.max(10, screenWidth - 230);
@@ -2521,8 +2658,8 @@ function DrawToolbar({
2521
2658
  },
2522
2659
  [insets.top, screenHeight, screenWidth]
2523
2660
  );
2524
- const panResponder = React13.useMemo(
2525
- () => import_react_native13.PanResponder.create({
2661
+ const panResponder = React15.useMemo(
2662
+ () => import_react_native15.PanResponder.create({
2526
2663
  onStartShouldSetPanResponder: () => false,
2527
2664
  onMoveShouldSetPanResponder: (_e, g) => Math.abs(g.dx) > 5 || Math.abs(g.dy) > 5,
2528
2665
  onPanResponderGrant: () => {
@@ -2534,7 +2671,7 @@ function DrawToolbar({
2534
2671
  },
2535
2672
  onPanResponderRelease: () => {
2536
2673
  const next = clamp2(currentPos.current.x, currentPos.current.y);
2537
- import_react_native13.Animated.spring(pos, { toValue: next, useNativeDriver: true }).start();
2674
+ import_react_native15.Animated.spring(pos, { toValue: next, useNativeDriver: true }).start();
2538
2675
  }
2539
2676
  }),
2540
2677
  [clamp2, pos]
@@ -2549,9 +2686,9 @@ function DrawToolbar({
2549
2686
  children
2550
2687
  }) {
2551
2688
  const isDisabled = Boolean(disabled) || Boolean(capturingDisabled);
2552
- const [pressed, setPressed] = React13.useState(false);
2553
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2554
- import_react_native13.View,
2689
+ const [pressed, setPressed] = React15.useState(false);
2690
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2691
+ import_react_native15.View,
2555
2692
  {
2556
2693
  style: {
2557
2694
  width: 28,
@@ -2562,8 +2699,8 @@ function DrawToolbar({
2562
2699
  backgroundColor,
2563
2700
  opacity: isDisabled ? 0.5 : pressed ? 0.85 : 1
2564
2701
  },
2565
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2566
- import_react_native13.Pressable,
2702
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2703
+ import_react_native15.Pressable,
2567
2704
  {
2568
2705
  accessibilityRole: "button",
2569
2706
  accessibilityLabel,
@@ -2579,8 +2716,8 @@ function DrawToolbar({
2579
2716
  }
2580
2717
  );
2581
2718
  }
2582
- return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2583
- import_react_native13.Animated.View,
2719
+ return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2720
+ import_react_native15.Animated.View,
2584
2721
  {
2585
2722
  style: [
2586
2723
  {
@@ -2596,8 +2733,8 @@ function DrawToolbar({
2596
2733
  style
2597
2734
  ],
2598
2735
  ...panResponder.panHandlers,
2599
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2600
- import_react_native13.View,
2736
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2737
+ import_react_native15.View,
2601
2738
  {
2602
2739
  style: {
2603
2740
  backgroundColor: "#F43F5E",
@@ -2605,9 +2742,9 @@ function DrawToolbar({
2605
2742
  padding: 12,
2606
2743
  minWidth: 220
2607
2744
  },
2608
- children: /* @__PURE__ */ (0, import_jsx_runtime11.jsxs)(import_react_native13.View, { style: { flexDirection: "row", alignItems: "center", gap: 8 }, children: [
2609
- renderDragHandle ? renderDragHandle() : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react_native.GripVertical, { size: 20, color: "rgba(255, 255, 255, 0.6)" }),
2610
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2745
+ children: /* @__PURE__ */ (0, import_jsx_runtime13.jsxs)(import_react_native15.View, { style: { flexDirection: "row", alignItems: "center", gap: 8 }, children: [
2746
+ renderDragHandle ? renderDragHandle() : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react_native.GripVertical, { size: 20, color: "rgba(255, 255, 255, 0.6)" }),
2747
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2611
2748
  DrawColorPicker,
2612
2749
  {
2613
2750
  colors,
@@ -2623,8 +2760,8 @@ function DrawToolbar({
2623
2760
  }
2624
2761
  }
2625
2762
  ),
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 } }),
2627
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2763
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native15.View, { style: { width: 1, height: 20, backgroundColor: "rgba(255, 255, 255, 0.3)", marginHorizontal: 4 } }),
2764
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2628
2765
  CircleActionButton,
2629
2766
  {
2630
2767
  accessibilityLabel: "Undo",
@@ -2635,10 +2772,10 @@ function DrawToolbar({
2635
2772
  void impact("light");
2636
2773
  onUndo();
2637
2774
  },
2638
- children: renderUndoIcon ? renderUndoIcon() : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react_native.Undo2, { size: 16, color: canUndo ? "#FFFFFF" : "rgba(255,255,255,0.4)" })
2775
+ children: renderUndoIcon ? renderUndoIcon() : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react_native.Undo2, { size: 16, color: canUndo ? "#FFFFFF" : "rgba(255,255,255,0.4)" })
2639
2776
  }
2640
2777
  ),
2641
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2778
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2642
2779
  CircleActionButton,
2643
2780
  {
2644
2781
  accessibilityLabel: "Cancel",
@@ -2648,10 +2785,10 @@ function DrawToolbar({
2648
2785
  void impact("medium");
2649
2786
  onCancel();
2650
2787
  },
2651
- children: renderCancelIcon ? renderCancelIcon() : /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_lucide_react_native.X, { size: 16, color: "#FFFFFF" })
2788
+ children: renderCancelIcon ? renderCancelIcon() : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react_native.X, { size: 16, color: "#FFFFFF" })
2652
2789
  }
2653
2790
  ),
2654
- /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
2791
+ /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
2655
2792
  CircleActionButton,
2656
2793
  {
2657
2794
  accessibilityLabel: "Done",
@@ -2661,7 +2798,7 @@ function DrawToolbar({
2661
2798
  void impact("medium");
2662
2799
  onDone();
2663
2800
  },
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" })
2801
+ children: capturing ? /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_react_native15.ActivityIndicator, { color: "#FFFFFF", size: "small" }) : renderDoneIcon ? renderDoneIcon() : /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(import_lucide_react_native.Check, { size: 16, color: "#FFFFFF" })
2665
2802
  }
2666
2803
  )
2667
2804
  ] })
@@ -2672,7 +2809,7 @@ function DrawToolbar({
2672
2809
  }
2673
2810
 
2674
2811
  // src/components/draw/DrawModeOverlay.tsx
2675
- var import_jsx_runtime12 = require("react/jsx-runtime");
2812
+ var import_jsx_runtime14 = require("react/jsx-runtime");
2676
2813
  function DrawModeOverlay({
2677
2814
  visible,
2678
2815
  captureTargetRef,
@@ -2687,7 +2824,7 @@ function DrawModeOverlay({
2687
2824
  renderDragHandle
2688
2825
  }) {
2689
2826
  const theme = useTheme();
2690
- const defaultPalette = React14.useMemo(
2827
+ const defaultPalette = React16.useMemo(
2691
2828
  () => [
2692
2829
  "#EF4444",
2693
2830
  // Red
@@ -2705,11 +2842,11 @@ function DrawModeOverlay({
2705
2842
  []
2706
2843
  );
2707
2844
  const colors = palette && palette.length > 0 ? palette : defaultPalette;
2708
- const [selectedColor, setSelectedColor] = React14.useState(colors[0] ?? "#EF4444");
2709
- const [strokes, setStrokes] = React14.useState([]);
2710
- const [capturing, setCapturing] = React14.useState(false);
2711
- const [hideUi, setHideUi] = React14.useState(false);
2712
- React14.useEffect(() => {
2845
+ const [selectedColor, setSelectedColor] = React16.useState(colors[0] ?? "#EF4444");
2846
+ const [strokes, setStrokes] = React16.useState([]);
2847
+ const [capturing, setCapturing] = React16.useState(false);
2848
+ const [hideUi, setHideUi] = React16.useState(false);
2849
+ React16.useEffect(() => {
2713
2850
  if (!visible) return;
2714
2851
  setStrokes([]);
2715
2852
  setSelectedColor(colors[0] ?? "#EF4444");
@@ -2717,14 +2854,14 @@ function DrawModeOverlay({
2717
2854
  setHideUi(false);
2718
2855
  }, [colors, visible]);
2719
2856
  const canUndo = strokes.length > 0;
2720
- const handleUndo = React14.useCallback(() => {
2857
+ const handleUndo = React16.useCallback(() => {
2721
2858
  setStrokes((prev) => prev.slice(0, -1));
2722
2859
  }, []);
2723
- const handleCancel = React14.useCallback(() => {
2860
+ const handleCancel = React16.useCallback(() => {
2724
2861
  setStrokes([]);
2725
2862
  onCancel();
2726
2863
  }, [onCancel]);
2727
- const handleDone = React14.useCallback(async () => {
2864
+ const handleDone = React16.useCallback(async () => {
2728
2865
  if (!captureTargetRef.current || capturing) return;
2729
2866
  try {
2730
2867
  setCapturing(true);
@@ -2746,9 +2883,9 @@ function DrawModeOverlay({
2746
2883
  }
2747
2884
  }, [captureTargetRef, capturing, onCapture]);
2748
2885
  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: [
2750
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(EdgeGlowFrame, { visible: !hideUi, role: "danger", thickness: 50, intensity: 1 }),
2751
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2886
+ return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native16.View, { style: [import_react_native16.StyleSheet.absoluteFill, styles3.root, style], pointerEvents: "box-none", children: [
2887
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(EdgeGlowFrame, { visible: !hideUi, role: "danger", thickness: 50, intensity: 1 }),
2888
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2752
2889
  DrawSurface,
2753
2890
  {
2754
2891
  color: selectedColor,
@@ -2757,7 +2894,7 @@ function DrawModeOverlay({
2757
2894
  onAddStroke: (stroke) => setStrokes((prev) => [...prev, stroke])
2758
2895
  }
2759
2896
  ),
2760
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
2897
+ /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
2761
2898
  DrawToolbar,
2762
2899
  {
2763
2900
  hidden: hideUi,
@@ -2777,32 +2914,32 @@ function DrawModeOverlay({
2777
2914
  )
2778
2915
  ] });
2779
2916
  }
2780
- var styles3 = import_react_native14.StyleSheet.create({
2917
+ var styles3 = import_react_native16.StyleSheet.create({
2781
2918
  root: {
2782
2919
  zIndex: 9999
2783
2920
  }
2784
2921
  });
2785
2922
 
2786
2923
  // src/components/comments/AppCommentsSheet.tsx
2787
- var React21 = __toESM(require("react"));
2788
- var import_react_native20 = require("react-native");
2924
+ var React23 = __toESM(require("react"));
2925
+ var import_react_native22 = require("react-native");
2789
2926
  var import_bottom_sheet3 = require("@gorhom/bottom-sheet");
2790
2927
  var import_react_native_safe_area_context3 = require("react-native-safe-area-context");
2791
- var import_liquid_glass4 = require("@callstack/liquid-glass");
2928
+ var import_liquid_glass5 = require("@callstack/liquid-glass");
2792
2929
  var import_lucide_react_native4 = require("lucide-react-native");
2793
2930
 
2794
2931
  // src/components/chat/ChatComposer.tsx
2795
- var React16 = __toESM(require("react"));
2796
- var import_react_native16 = require("react-native");
2797
- var import_liquid_glass3 = require("@callstack/liquid-glass");
2932
+ var React18 = __toESM(require("react"));
2933
+ var import_react_native18 = require("react-native");
2934
+ var import_liquid_glass4 = require("@callstack/liquid-glass");
2798
2935
  var import_lucide_react_native3 = require("lucide-react-native");
2799
2936
 
2800
2937
  // src/components/chat/MultilineTextInput.tsx
2801
- var React15 = __toESM(require("react"));
2802
- var import_react_native15 = require("react-native");
2938
+ var React17 = __toESM(require("react"));
2939
+ var import_react_native17 = require("react-native");
2803
2940
  var import_bottom_sheet2 = require("@gorhom/bottom-sheet");
2804
- var import_jsx_runtime13 = require("react/jsx-runtime");
2805
- var MultilineTextInput = React15.forwardRef(function MultilineTextInput2({ useBottomSheetTextInput = false, placeholder, placeholderTextColor, style, ...props }, ref) {
2941
+ var import_jsx_runtime15 = require("react/jsx-runtime");
2942
+ var MultilineTextInput = React17.forwardRef(function MultilineTextInput2({ useBottomSheetTextInput = false, placeholder, placeholderTextColor, style, ...props }, ref) {
2806
2943
  const theme = useTheme();
2807
2944
  const baseStyle = {
2808
2945
  minHeight: 44,
@@ -2822,12 +2959,12 @@ var MultilineTextInput = React15.forwardRef(function MultilineTextInput2({ useBo
2822
2959
  style: [baseStyle, style],
2823
2960
  textAlignVertical: "top"
2824
2961
  };
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 });
2962
+ return useBottomSheetTextInput ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_bottom_sheet2.BottomSheetTextInput, { ref, ...commonProps }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_react_native17.TextInput, { ref, ...commonProps });
2826
2963
  });
2827
2964
 
2828
2965
  // src/components/icons/StudioIcons.tsx
2829
2966
  var import_lucide_react_native2 = require("lucide-react-native");
2830
- var import_jsx_runtime14 = require("react/jsx-runtime");
2967
+ var import_jsx_runtime16 = require("react/jsx-runtime");
2831
2968
  function useResolvedIconColor(token) {
2832
2969
  const theme = useTheme();
2833
2970
  switch (token) {
@@ -2851,7 +2988,7 @@ function useResolvedIconColor(token) {
2851
2988
  function makeIcon(Comp) {
2852
2989
  return function StudioIcon({ size = 20, strokeWidth = 2, colorToken = "floatingContent", ...rest }) {
2853
2990
  const color = useResolvedIconColor(colorToken);
2854
- return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Comp, { size, strokeWidth, color, ...rest });
2991
+ return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Comp, { size, strokeWidth, color, ...rest });
2855
2992
  };
2856
2993
  }
2857
2994
  var IconHome = makeIcon(import_lucide_react_native2.Home);
@@ -2867,17 +3004,17 @@ var IconArrowDown = makeIcon(import_lucide_react_native2.ArrowDown);
2867
3004
  var IconApprove = makeIcon(import_lucide_react_native2.Check);
2868
3005
 
2869
3006
  // src/components/chat/ChatComposer.tsx
2870
- var import_jsx_runtime15 = require("react/jsx-runtime");
3007
+ var import_jsx_runtime17 = require("react/jsx-runtime");
2871
3008
  var THUMBNAIL_HEIGHT = 90;
2872
3009
  function AspectRatioThumbnail({
2873
3010
  uri,
2874
3011
  onRemove,
2875
3012
  renderRemoveIcon
2876
3013
  }) {
2877
- 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,
3014
+ const [aspectRatio, setAspectRatio] = React18.useState(1);
3015
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react_native18.View, { style: { height: THUMBNAIL_HEIGHT, aspectRatio, position: "relative" }, children: [
3016
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native18.View, { style: { flex: 1, borderRadius: 8, overflow: "hidden" }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3017
+ import_react_native18.Image,
2881
3018
  {
2882
3019
  source: { uri },
2883
3020
  style: { width: "100%", height: "100%" },
@@ -2889,8 +3026,8 @@ function AspectRatioThumbnail({
2889
3026
  }
2890
3027
  }
2891
3028
  ) }),
2892
- onRemove ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2893
- import_react_native16.Pressable,
3029
+ onRemove ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3030
+ import_react_native18.Pressable,
2894
3031
  {
2895
3032
  style: {
2896
3033
  position: "absolute",
@@ -2906,7 +3043,7 @@ function AspectRatioThumbnail({
2906
3043
  },
2907
3044
  onPress: onRemove,
2908
3045
  hitSlop: 10,
2909
- children: renderRemoveIcon ? renderRemoveIcon() : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(IconClose, { size: 12, colorToken: "onPrimary" })
3046
+ children: renderRemoveIcon ? renderRemoveIcon() : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconClose, { size: 12, colorToken: "onPrimary" })
2910
3047
  }
2911
3048
  ) : null
2912
3049
  ] });
@@ -2931,19 +3068,19 @@ function ChatComposer({
2931
3068
  style
2932
3069
  }) {
2933
3070
  const theme = useTheme();
2934
- const [internal, setInternal] = React16.useState("");
3071
+ const [internal, setInternal] = React18.useState("");
2935
3072
  const text = value ?? internal;
2936
3073
  const setText = onChangeValue ?? setInternal;
2937
3074
  const hasAttachments = attachments.length > 0;
2938
3075
  const hasText = text.trim().length > 0;
2939
3076
  const composerMinHeight = hasAttachments ? THUMBNAIL_HEIGHT + 44 + 24 : 44;
2940
3077
  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;
2943
- const [sendPressed, setSendPressed] = React16.useState(false);
2944
- const inputRef = React16.useRef(null);
2945
- const prevAutoFocusRef = React16.useRef(false);
2946
- React16.useEffect(() => {
3078
+ const maxInputHeight = React18.useMemo(() => import_react_native18.Dimensions.get("window").height * 0.5, []);
3079
+ const shakeAnim = React18.useRef(new import_react_native18.Animated.Value(0)).current;
3080
+ const [sendPressed, setSendPressed] = React18.useState(false);
3081
+ const inputRef = React18.useRef(null);
3082
+ const prevAutoFocusRef = React18.useRef(false);
3083
+ React18.useEffect(() => {
2947
3084
  const shouldFocus = autoFocus && !prevAutoFocusRef.current && !disabled && !sending;
2948
3085
  prevAutoFocusRef.current = autoFocus;
2949
3086
  if (!shouldFocus) return;
@@ -2953,17 +3090,17 @@ function ChatComposer({
2953
3090
  }, 75);
2954
3091
  return () => clearTimeout(t);
2955
3092
  }, [autoFocus, disabled, sending]);
2956
- const triggerShake = React16.useCallback(() => {
3093
+ const triggerShake = React18.useCallback(() => {
2957
3094
  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 })
3095
+ import_react_native18.Animated.sequence([
3096
+ import_react_native18.Animated.timing(shakeAnim, { toValue: 10, duration: 50, useNativeDriver: true }),
3097
+ import_react_native18.Animated.timing(shakeAnim, { toValue: -10, duration: 50, useNativeDriver: true }),
3098
+ import_react_native18.Animated.timing(shakeAnim, { toValue: 10, duration: 50, useNativeDriver: true }),
3099
+ import_react_native18.Animated.timing(shakeAnim, { toValue: -10, duration: 50, useNativeDriver: true }),
3100
+ import_react_native18.Animated.timing(shakeAnim, { toValue: 0, duration: 50, useNativeDriver: true })
2964
3101
  ]).start();
2965
3102
  }, [shakeAnim]);
2966
- const handleSend = React16.useCallback(async () => {
3103
+ const handleSend = React18.useCallback(async () => {
2967
3104
  if (isButtonDisabled) return;
2968
3105
  if (!hasText) {
2969
3106
  triggerShake();
@@ -2975,33 +3112,34 @@ function ChatComposer({
2975
3112
  }, [attachments, hasText, isButtonDisabled, onSend, setText, text, triggerShake]);
2976
3113
  const textareaBgColor = theme.scheme === "dark" ? "#18181B" : "#F6F6F6";
2977
3114
  const placeholderTextColor = theme.scheme === "dark" ? "#A1A1AA" : "#71717A";
2978
- return /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
2979
- import_react_native16.View,
3115
+ const sendBg = withAlpha(theme.colors.primary, isButtonDisabled ? 0.6 : sendPressed ? 0.9 : 1);
3116
+ return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3117
+ import_react_native18.View,
2980
3118
  {
2981
3119
  style: [{ paddingHorizontal: 16, paddingBottom: 12, paddingTop: 8 }, style],
2982
3120
  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)(
2985
- import_liquid_glass3.LiquidGlassView,
3121
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(import_react_native18.View, { style: { flexDirection: "row", alignItems: "flex-end", gap: 8 }, children: [
3122
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native18.Animated.View, { style: { flex: 1, transform: [{ translateX: shakeAnim }] }, children: /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3123
+ ResettableLiquidGlassView,
2986
3124
  {
2987
3125
  style: [
2988
3126
  // LiquidGlassView doesn't reliably auto-size to children; ensure enough height for the
2989
3127
  // thumbnail strip when attachments are present.
2990
3128
  { borderRadius: 24, flex: 1, minHeight: composerMinHeight },
2991
- !import_liquid_glass3.isLiquidGlassSupported && { backgroundColor: textareaBgColor }
3129
+ !import_liquid_glass4.isLiquidGlassSupported && { backgroundColor: textareaBgColor }
2992
3130
  ],
2993
3131
  interactive: true,
2994
3132
  effect: "clear",
2995
3133
  children: [
2996
- hasAttachments ? /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
2997
- import_react_native16.ScrollView,
3134
+ hasAttachments ? /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3135
+ import_react_native18.ScrollView,
2998
3136
  {
2999
3137
  horizontal: true,
3000
3138
  showsHorizontalScrollIndicator: false,
3001
3139
  keyboardShouldPersistTaps: "handled",
3002
3140
  contentContainerStyle: { gap: 8, paddingHorizontal: 12, paddingTop: 12 },
3003
3141
  children: [
3004
- attachments.map((uri, index) => /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3142
+ attachments.map((uri, index) => /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3005
3143
  AspectRatioThumbnail,
3006
3144
  {
3007
3145
  uri,
@@ -3010,8 +3148,8 @@ function ChatComposer({
3010
3148
  },
3011
3149
  `attachment-${index}`
3012
3150
  )),
3013
- onAddAttachment ? renderAddAttachment ? renderAddAttachment() : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3014
- import_react_native16.Pressable,
3151
+ onAddAttachment ? renderAddAttachment ? renderAddAttachment() : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3152
+ import_react_native18.Pressable,
3015
3153
  {
3016
3154
  style: {
3017
3155
  height: THUMBNAIL_HEIGHT,
@@ -3025,13 +3163,13 @@ function ChatComposer({
3025
3163
  backgroundColor: "rgba(255, 255, 255, 0.05)"
3026
3164
  },
3027
3165
  onPress: onAddAttachment,
3028
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(import_lucide_react_native3.Plus, { size: 24, color: "rgba(255, 255, 255, 0.5)" })
3166
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_lucide_react_native3.Plus, { size: 24, color: "rgba(255, 255, 255, 0.5)" })
3029
3167
  }
3030
3168
  ) : null
3031
3169
  ]
3032
3170
  }
3033
3171
  ) : null,
3034
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3172
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3035
3173
  MultilineTextInput,
3036
3174
  {
3037
3175
  ref: inputRef,
@@ -3056,25 +3194,24 @@ function ChatComposer({
3056
3194
  ]
3057
3195
  }
3058
3196
  ) }),
3059
- /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3060
- import_liquid_glass3.LiquidGlassView,
3197
+ /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3198
+ ResettableLiquidGlassView,
3061
3199
  {
3062
- style: [{ borderRadius: 100 }, !import_liquid_glass3.isLiquidGlassSupported && { backgroundColor: textareaBgColor }],
3200
+ style: [{ borderRadius: 100 }, !import_liquid_glass4.isLiquidGlassSupported && { backgroundColor: textareaBgColor }],
3063
3201
  interactive: true,
3064
3202
  effect: "clear",
3065
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3066
- import_react_native16.View,
3203
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3204
+ import_react_native18.View,
3067
3205
  {
3068
3206
  style: {
3069
3207
  width: 44,
3070
3208
  height: 44,
3071
3209
  borderRadius: 22,
3072
3210
  overflow: "hidden",
3073
- backgroundColor: theme.colors.primary,
3074
- opacity: isButtonDisabled ? 0.6 : sendPressed ? 0.9 : 1
3211
+ backgroundColor: sendBg
3075
3212
  },
3076
- children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
3077
- import_react_native16.Pressable,
3213
+ children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
3214
+ import_react_native18.Pressable,
3078
3215
  {
3079
3216
  accessibilityRole: "button",
3080
3217
  accessibilityLabel: "Send",
@@ -3083,7 +3220,7 @@ function ChatComposer({
3083
3220
  onPressIn: () => setSendPressed(true),
3084
3221
  onPressOut: () => setSendPressed(false),
3085
3222
  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" })
3223
+ children: sending ? /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(import_react_native18.ActivityIndicator, {}) : renderSendIcon ? renderSendIcon() : /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(IconChevronRight, { size: 20, colorToken: "onPrimary" })
3087
3224
  }
3088
3225
  )
3089
3226
  }
@@ -3096,12 +3233,12 @@ function ChatComposer({
3096
3233
  }
3097
3234
 
3098
3235
  // src/components/comments/CommentRow.tsx
3099
- var React17 = __toESM(require("react"));
3100
- var import_react_native18 = require("react-native");
3236
+ var React19 = __toESM(require("react"));
3237
+ var import_react_native20 = require("react-native");
3101
3238
 
3102
3239
  // src/components/primitives/Avatar.tsx
3103
- var import_react_native17 = require("react-native");
3104
- var import_jsx_runtime16 = require("react/jsx-runtime");
3240
+ var import_react_native19 = require("react-native");
3241
+ var import_jsx_runtime18 = require("react/jsx-runtime");
3105
3242
  function initialsFrom(name) {
3106
3243
  var _a, _b;
3107
3244
  const trimmed = (name ?? "").trim();
@@ -3119,8 +3256,8 @@ function Avatar({
3119
3256
  const theme = useTheme();
3120
3257
  const radius = size / 2;
3121
3258
  const fallbackBg = fallbackBackgroundColor ?? theme.colors.neutral;
3122
- return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3123
- import_react_native17.View,
3259
+ return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3260
+ import_react_native19.View,
3124
3261
  {
3125
3262
  style: [
3126
3263
  {
@@ -3134,14 +3271,14 @@ function Avatar({
3134
3271
  },
3135
3272
  style
3136
3273
  ],
3137
- children: uri ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
3138
- import_react_native17.Image,
3274
+ children: uri ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3275
+ import_react_native19.Image,
3139
3276
  {
3140
3277
  source: { uri },
3141
3278
  style: [{ width: size, height: size }, imageStyle],
3142
3279
  resizeMode: "cover"
3143
3280
  }
3144
- ) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text, { variant: "caption", style: { color: theme.colors.onNeutral }, children: initialsFrom(name) })
3281
+ ) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { variant: "caption", style: { color: theme.colors.onNeutral }, children: initialsFrom(name) })
3145
3282
  }
3146
3283
  );
3147
3284
  }
@@ -3165,12 +3302,12 @@ function formatTimeAgo(iso) {
3165
3302
  }
3166
3303
 
3167
3304
  // src/components/comments/CommentRow.tsx
3168
- var import_jsx_runtime17 = require("react/jsx-runtime");
3305
+ var import_jsx_runtime19 = require("react/jsx-runtime");
3169
3306
  function CommentRow({ comment, showDivider }) {
3170
3307
  const theme = useTheme();
3171
- const [authorName, setAuthorName] = React17.useState(null);
3172
- const [authorAvatar, setAuthorAvatar] = React17.useState(null);
3173
- React17.useEffect(() => {
3308
+ const [authorName, setAuthorName] = React19.useState(null);
3309
+ const [authorAvatar, setAuthorAvatar] = React19.useState(null);
3310
+ React19.useEffect(() => {
3174
3311
  let cancelled = false;
3175
3312
  (async () => {
3176
3313
  try {
@@ -3185,8 +3322,8 @@ function CommentRow({ comment, showDivider }) {
3185
3322
  cancelled = true;
3186
3323
  };
3187
3324
  }, [comment.authorId]);
3188
- return /* @__PURE__ */ (0, import_jsx_runtime17.jsxs)(
3189
- import_react_native18.View,
3325
+ return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
3326
+ import_react_native20.View,
3190
3327
  {
3191
3328
  style: {
3192
3329
  flexDirection: "row",
@@ -3196,13 +3333,13 @@ function CommentRow({ comment, showDivider }) {
3196
3333
  borderBottomColor: withAlpha(theme.colors.border, 0.5)
3197
3334
  },
3198
3335
  children: [
3199
- /* @__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: [
3202
- /* @__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
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { style: { fontSize: 12, lineHeight: 16, color: theme.colors.textMuted }, children: formatTimeAgo(comment.createdAt) })
3336
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Avatar, { size: 32, uri: authorAvatar, name: authorName ?? comment.authorId, style: { marginTop: 6 } }),
3337
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native20.View, { style: { flex: 1, minWidth: 0, gap: 4 }, children: [
3338
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(import_react_native20.View, { style: { flexDirection: "row", alignItems: "center", gap: theme.spacing.sm }, children: [
3339
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Text, { style: { fontSize: 14, lineHeight: 18, fontWeight: theme.typography.fontWeight.bold, color: theme.colors.text }, children: authorName ?? "Unknown User" }),
3340
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Text, { style: { fontSize: 12, lineHeight: 16, color: theme.colors.textMuted }, children: formatTimeAgo(comment.createdAt) })
3204
3341
  ] }),
3205
- /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(Text, { style: { fontSize: 14, lineHeight: 20, color: theme.colors.text }, children: comment.body ?? comment.description ?? "" })
3342
+ /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Text, { style: { fontSize: 14, lineHeight: 20, color: theme.colors.text }, children: comment.body ?? comment.description ?? "" })
3206
3343
  ] })
3207
3344
  ]
3208
3345
  }
@@ -3210,7 +3347,7 @@ function CommentRow({ comment, showDivider }) {
3210
3347
  }
3211
3348
 
3212
3349
  // src/components/comments/useAppComments.ts
3213
- var React18 = __toESM(require("react"));
3350
+ var React20 = __toESM(require("react"));
3214
3351
 
3215
3352
  // src/data/comments/remote.ts
3216
3353
  var AppCommentsRemoteDataSourceImpl = class extends BaseRemote {
@@ -3282,18 +3419,18 @@ var appCommentsRepository = new AppCommentsRepositoryImpl(appCommentsRemoteDataS
3282
3419
 
3283
3420
  // src/components/comments/useAppComments.ts
3284
3421
  function useAppComments(appId) {
3285
- const [comments, setComments] = React18.useState([]);
3286
- const [loading, setLoading] = React18.useState(false);
3287
- const [sending, setSending] = React18.useState(false);
3288
- const [error, setError] = React18.useState(null);
3289
- const sortByCreatedAtAsc = React18.useCallback((items) => {
3422
+ const [comments, setComments] = React20.useState([]);
3423
+ const [loading, setLoading] = React20.useState(false);
3424
+ const [sending, setSending] = React20.useState(false);
3425
+ const [error, setError] = React20.useState(null);
3426
+ const sortByCreatedAtAsc = React20.useCallback((items) => {
3290
3427
  return [...items].sort((a, b) => {
3291
3428
  const at = a.createdAt ? new Date(a.createdAt).getTime() : 0;
3292
3429
  const bt = b.createdAt ? new Date(b.createdAt).getTime() : 0;
3293
3430
  return at - bt;
3294
3431
  });
3295
3432
  }, []);
3296
- const refresh = React18.useCallback(async () => {
3433
+ const refresh = React20.useCallback(async () => {
3297
3434
  if (!appId) {
3298
3435
  setComments([]);
3299
3436
  return;
@@ -3310,10 +3447,10 @@ function useAppComments(appId) {
3310
3447
  setLoading(false);
3311
3448
  }
3312
3449
  }, [appId, sortByCreatedAtAsc]);
3313
- React18.useEffect(() => {
3450
+ React20.useEffect(() => {
3314
3451
  void refresh();
3315
3452
  }, [refresh]);
3316
- const create = React18.useCallback(
3453
+ const create = React20.useCallback(
3317
3454
  async (text) => {
3318
3455
  if (!appId) return;
3319
3456
  const trimmed = text.trim();
@@ -3336,11 +3473,11 @@ function useAppComments(appId) {
3336
3473
  }
3337
3474
 
3338
3475
  // src/components/comments/useAppDetails.ts
3339
- var React19 = __toESM(require("react"));
3476
+ var React21 = __toESM(require("react"));
3340
3477
  function useAppDetails(appId) {
3341
- const [app, setApp] = React19.useState(null);
3342
- const [loading, setLoading] = React19.useState(false);
3343
- React19.useEffect(() => {
3478
+ const [app, setApp] = React21.useState(null);
3479
+ const [loading, setLoading] = React21.useState(false);
3480
+ React21.useEffect(() => {
3344
3481
  if (!appId) {
3345
3482
  setApp(null);
3346
3483
  return;
@@ -3365,14 +3502,14 @@ function useAppDetails(appId) {
3365
3502
  }
3366
3503
 
3367
3504
  // src/components/comments/useIosKeyboardSnapFix.ts
3368
- var React20 = __toESM(require("react"));
3369
- var import_react_native19 = require("react-native");
3505
+ var React22 = __toESM(require("react"));
3506
+ var import_react_native21 = require("react-native");
3370
3507
  function useIosKeyboardSnapFix(sheetRef, options) {
3371
- const [keyboardVisible, setKeyboardVisible] = React20.useState(false);
3372
- 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", () => {
3508
+ const [keyboardVisible, setKeyboardVisible] = React22.useState(false);
3509
+ React22.useEffect(() => {
3510
+ if (import_react_native21.Platform.OS !== "ios") return;
3511
+ const show = import_react_native21.Keyboard.addListener("keyboardWillShow", () => setKeyboardVisible(true));
3512
+ const hide = import_react_native21.Keyboard.addListener("keyboardWillHide", () => {
3376
3513
  var _a;
3377
3514
  setKeyboardVisible(false);
3378
3515
  const target = (options == null ? void 0 : options.targetIndex) ?? 1;
@@ -3393,20 +3530,20 @@ function useIosKeyboardSnapFix(sheetRef, options) {
3393
3530
  }
3394
3531
 
3395
3532
  // src/components/comments/AppCommentsSheet.tsx
3396
- var import_jsx_runtime18 = require("react/jsx-runtime");
3533
+ var import_jsx_runtime20 = require("react/jsx-runtime");
3397
3534
  function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3398
3535
  const theme = useTheme();
3399
3536
  const insets = (0, import_react_native_safe_area_context3.useSafeAreaInsets)();
3400
- const sheetRef = React21.useRef(null);
3401
- const snapPoints = React21.useMemo(() => ["50%", "90%"], []);
3402
- const currentIndexRef = React21.useRef(1);
3537
+ const sheetRef = React23.useRef(null);
3538
+ const snapPoints = React23.useMemo(() => ["50%", "90%"], []);
3539
+ const currentIndexRef = React23.useRef(1);
3403
3540
  const { comments, loading, sending, error, create, refresh } = useAppComments(appId);
3404
3541
  const { app, loading: loadingApp } = useAppDetails(appId);
3405
3542
  const { keyboardVisible } = useIosKeyboardSnapFix(sheetRef, {
3406
3543
  getCurrentIndex: () => currentIndexRef.current,
3407
3544
  targetIndex: 1
3408
3545
  });
3409
- React21.useEffect(() => {
3546
+ React23.useEffect(() => {
3410
3547
  var _a, _b;
3411
3548
  if (appId) {
3412
3549
  (_a = sheetRef.current) == null ? void 0 : _a.present();
@@ -3415,29 +3552,29 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3415
3552
  (_b = sheetRef.current) == null ? void 0 : _b.dismiss();
3416
3553
  }
3417
3554
  }, [appId, refresh]);
3418
- React21.useEffect(() => {
3555
+ React23.useEffect(() => {
3419
3556
  if (!appId) return;
3420
3557
  onCountChange == null ? void 0 : onCountChange(comments.length);
3421
3558
  }, [appId, comments.length, onCountChange]);
3422
- const renderBackdrop = React21.useCallback(
3423
- (props) => /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_bottom_sheet3.BottomSheetBackdrop, { ...props, disappearsOnIndex: -1, appearsOnIndex: 0, opacity: 0.5 }),
3559
+ const renderBackdrop = React23.useCallback(
3560
+ (props) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_bottom_sheet3.BottomSheetBackdrop, { ...props, disappearsOnIndex: -1, appearsOnIndex: 0, opacity: 0.5 }),
3424
3561
  []
3425
3562
  );
3426
- const handleChange = React21.useCallback(
3563
+ const handleChange = React23.useCallback(
3427
3564
  (index) => {
3428
3565
  currentIndexRef.current = index;
3429
3566
  if (index === -1) onClose();
3430
3567
  },
3431
3568
  [onClose]
3432
3569
  );
3433
- const handlePlay = React21.useCallback(async () => {
3570
+ const handlePlay = React23.useCallback(async () => {
3434
3571
  var _a;
3435
3572
  if (!appId) return;
3436
3573
  (_a = sheetRef.current) == null ? void 0 : _a.dismiss();
3437
3574
  await (onPlayApp == null ? void 0 : onPlayApp(appId));
3438
3575
  onClose();
3439
3576
  }, [appId, onClose, onPlayApp]);
3440
- return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3577
+ return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3441
3578
  import_bottom_sheet3.BottomSheetModal,
3442
3579
  {
3443
3580
  ref: sheetRef,
@@ -3448,17 +3585,17 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3448
3585
  onChange: handleChange,
3449
3586
  backgroundStyle: {
3450
3587
  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
3588
+ borderTopLeftRadius: import_react_native22.Platform.OS === "ios" ? 39 : 16,
3589
+ borderTopRightRadius: import_react_native22.Platform.OS === "ios" ? 39 : 16
3453
3590
  },
3454
3591
  handleIndicatorStyle: { backgroundColor: theme.colors.handleIndicator },
3455
3592
  keyboardBehavior: "interactive",
3456
3593
  keyboardBlurBehavior: "restore",
3457
3594
  android_keyboardInputMode: "adjustResize",
3458
3595
  topInset: insets.top,
3459
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_react_native20.View, { style: { flex: 1 }, children: [
3460
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
3461
- import_react_native20.View,
3596
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(import_react_native22.View, { style: { flex: 1 }, children: [
3597
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
3598
+ import_react_native22.View,
3462
3599
  {
3463
3600
  style: {
3464
3601
  flexDirection: "row",
@@ -3470,7 +3607,7 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3470
3607
  borderBottomColor: withAlpha(theme.colors.border, 0.1)
3471
3608
  },
3472
3609
  children: [
3473
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3610
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3474
3611
  Text,
3475
3612
  {
3476
3613
  numberOfLines: 1,
@@ -3484,29 +3621,28 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3484
3621
  children: loadingApp ? "Loading..." : (app == null ? void 0 : app.name) || "Comments"
3485
3622
  }
3486
3623
  ),
3487
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3488
- import_liquid_glass4.LiquidGlassView,
3624
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3625
+ ResettableLiquidGlassView,
3489
3626
  {
3490
3627
  style: [
3491
3628
  { borderRadius: 24 },
3492
- !import_liquid_glass4.isLiquidGlassSupported && { backgroundColor: theme.scheme === "dark" ? "#18181B" : "#F6F6F6" }
3629
+ !import_liquid_glass5.isLiquidGlassSupported && { backgroundColor: theme.scheme === "dark" ? "#18181B" : "#F6F6F6" }
3493
3630
  ],
3494
3631
  interactive: true,
3495
3632
  effect: "clear",
3496
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3497
- import_react_native20.View,
3633
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3634
+ import_react_native22.View,
3498
3635
  {
3499
3636
  style: {
3500
3637
  width: 32,
3501
3638
  height: 32,
3502
3639
  borderRadius: 999,
3503
- backgroundColor: theme.colors.primary,
3640
+ backgroundColor: withAlpha(theme.colors.primary, appId ? 1 : 0.5),
3504
3641
  alignItems: "center",
3505
- justifyContent: "center",
3506
- opacity: appId ? 1 : 0.5
3642
+ justifyContent: "center"
3507
3643
  },
3508
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3509
- import_react_native20.Pressable,
3644
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3645
+ import_react_native22.Pressable,
3510
3646
  {
3511
3647
  disabled: !appId,
3512
3648
  onPress: () => void handlePlay(),
@@ -3518,9 +3654,9 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3518
3654
  alignItems: "center",
3519
3655
  justifyContent: "center"
3520
3656
  },
3521
- pressed ? { opacity: 0.85 } : null
3657
+ pressed ? { transform: [{ scale: 0.96 }] } : null
3522
3658
  ],
3523
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react_native4.Play, { size: 16, color: theme.colors.onPrimary })
3659
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_lucide_react_native4.Play, { size: 16, color: theme.colors.onPrimary })
3524
3660
  }
3525
3661
  )
3526
3662
  }
@@ -3530,7 +3666,7 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3530
3666
  ]
3531
3667
  }
3532
3668
  ),
3533
- /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
3669
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
3534
3670
  import_bottom_sheet3.BottomSheetScrollView,
3535
3671
  {
3536
3672
  style: { flex: 1 },
@@ -3541,13 +3677,13 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3541
3677
  },
3542
3678
  keyboardShouldPersistTaps: "handled",
3543
3679
  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)),
3545
- error ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Text, { variant: "captionMuted", style: { marginTop: theme.spacing.lg }, children: "Failed to load comments." }) : null
3680
+ loading && comments.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native22.View, { style: { flex: 1, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native22.ActivityIndicator, {}) }) : comments.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(import_react_native22.View, { style: { flex: 1, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Text, { variant: "bodyMuted", style: { textAlign: "center" }, children: "No comments yet" }) }) : comments.map((c, idx) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(CommentRow, { comment: c, showDivider: idx < comments.length - 1 }, c.id)),
3681
+ error ? /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Text, { variant: "captionMuted", style: { marginTop: theme.spacing.lg }, children: "Failed to load comments." }) : null
3546
3682
  ]
3547
3683
  }
3548
3684
  ),
3549
- /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3550
- import_react_native20.View,
3685
+ /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3686
+ import_react_native22.View,
3551
3687
  {
3552
3688
  style: {
3553
3689
  position: "absolute",
@@ -3556,12 +3692,12 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3556
3692
  bottom: 0,
3557
3693
  paddingHorizontal: theme.spacing.lg,
3558
3694
  paddingTop: theme.spacing.sm,
3559
- paddingBottom: import_react_native20.Platform.OS === "ios" ? keyboardVisible ? theme.spacing.lg : insets.bottom : insets.bottom + 10,
3695
+ paddingBottom: import_react_native22.Platform.OS === "ios" ? keyboardVisible ? theme.spacing.lg : insets.bottom : insets.bottom + 10,
3560
3696
  borderTopWidth: 1,
3561
3697
  borderTopColor: withAlpha(theme.colors.border, 0.1),
3562
3698
  backgroundColor: withAlpha(theme.colors.background, 0.8)
3563
3699
  },
3564
- children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
3700
+ children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
3565
3701
  ChatComposer,
3566
3702
  {
3567
3703
  placeholder: "Write a comment...",
@@ -3570,7 +3706,7 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3570
3706
  useBottomSheetTextInput: true,
3571
3707
  onSend: async (text) => {
3572
3708
  await create(text);
3573
- import_react_native20.Keyboard.dismiss();
3709
+ import_react_native22.Keyboard.dismiss();
3574
3710
  }
3575
3711
  }
3576
3712
  )
@@ -3582,17 +3718,17 @@ function AppCommentsSheet({ appId, onClose, onCountChange, onPlayApp }) {
3582
3718
  }
3583
3719
 
3584
3720
  // src/studio/ui/PreviewPanel.tsx
3585
- var import_react_native41 = require("react-native");
3721
+ var import_react_native43 = require("react-native");
3586
3722
 
3587
3723
  // src/components/preview/PreviewPage.tsx
3588
- var import_react_native21 = require("react-native");
3724
+ var import_react_native23 = require("react-native");
3589
3725
  var import_bottom_sheet4 = require("@gorhom/bottom-sheet");
3590
- var import_jsx_runtime19 = require("react/jsx-runtime");
3726
+ var import_jsx_runtime21 = require("react/jsx-runtime");
3591
3727
  function PreviewPage({ header, children, contentStyle }) {
3592
3728
  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,
3595
- /* @__PURE__ */ (0, import_jsx_runtime19.jsx)(
3729
+ return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_react_native23.View, { style: { flex: 1 }, children: [
3730
+ header ? /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(import_react_native23.View, { children: header }) : null,
3731
+ /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3596
3732
  import_bottom_sheet4.BottomSheetScrollView,
3597
3733
  {
3598
3734
  style: { flex: 1 },
@@ -3611,15 +3747,15 @@ function PreviewPage({ header, children, contentStyle }) {
3611
3747
  }
3612
3748
 
3613
3749
  // src/studio/ui/preview-panel/PreviewPanelHeader.tsx
3614
- var import_react_native24 = require("react-native");
3750
+ var import_react_native26 = require("react-native");
3615
3751
 
3616
3752
  // src/components/studio-sheet/StudioSheetHeader.tsx
3617
- var import_react_native22 = require("react-native");
3618
- var import_jsx_runtime20 = require("react/jsx-runtime");
3753
+ var import_react_native24 = require("react-native");
3754
+ var import_jsx_runtime22 = require("react/jsx-runtime");
3619
3755
  function StudioSheetHeader({ left, center, right, style }) {
3620
3756
  const theme = useTheme();
3621
- return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
3622
- import_react_native22.View,
3757
+ return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
3758
+ import_react_native24.View,
3623
3759
  {
3624
3760
  style: [
3625
3761
  {
@@ -3632,19 +3768,19 @@ function StudioSheetHeader({ left, center, right, style }) {
3632
3768
  style
3633
3769
  ],
3634
3770
  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 })
3771
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native24.View, { style: { flexDirection: "row", alignItems: "center" }, children: left }),
3772
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native24.View, { style: { flex: 1, alignItems: "center" }, children: center }),
3773
+ /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(import_react_native24.View, { style: { flexDirection: "row", alignItems: "center" }, children: right })
3638
3774
  ]
3639
3775
  }
3640
3776
  );
3641
3777
  }
3642
3778
 
3643
3779
  // src/components/studio-sheet/StudioSheetHeaderIconButton.tsx
3644
- var React22 = __toESM(require("react"));
3645
- var import_react_native23 = require("react-native");
3646
- var import_liquid_glass5 = require("@callstack/liquid-glass");
3647
- var import_jsx_runtime21 = require("react/jsx-runtime");
3780
+ var React24 = __toESM(require("react"));
3781
+ var import_react_native25 = require("react-native");
3782
+ var import_liquid_glass6 = require("@callstack/liquid-glass");
3783
+ var import_jsx_runtime23 = require("react/jsx-runtime");
3648
3784
  function StudioSheetHeaderIconButton({
3649
3785
  onPress,
3650
3786
  disabled,
@@ -3656,19 +3792,20 @@ function StudioSheetHeaderIconButton({
3656
3792
  }) {
3657
3793
  const theme = useTheme();
3658
3794
  const size = 44;
3659
- const [pressed, setPressed] = React22.useState(false);
3795
+ const [pressed, setPressed] = React24.useState(false);
3660
3796
  const solidBg = intent === "danger" ? theme.colors.danger : intent === "primary" ? theme.colors.primary : theme.colors.neutral;
3661
3797
  const glassFallbackBg = theme.scheme === "dark" ? "#18181B" : "#F6F6F6";
3662
3798
  const glassInnerBg = intent === "danger" ? theme.colors.danger : theme.colors.primary;
3663
3799
  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)(
3665
- import_liquid_glass5.LiquidGlassView,
3800
+ const glassBg = withAlpha(glassInnerBg, resolvedOpacity);
3801
+ return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(import_react_native25.View, { style, children: appearance === "glass" ? /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3802
+ ResettableLiquidGlassView,
3666
3803
  {
3667
- style: [{ borderRadius: 100 }, !import_liquid_glass5.isLiquidGlassSupported && { backgroundColor: glassFallbackBg }],
3804
+ style: [{ borderRadius: 100 }, !import_liquid_glass6.isLiquidGlassSupported && { backgroundColor: glassFallbackBg }],
3668
3805
  interactive: true,
3669
3806
  effect: "clear",
3670
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3671
- import_react_native23.View,
3807
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3808
+ import_react_native25.View,
3672
3809
  {
3673
3810
  style: {
3674
3811
  width: size,
@@ -3676,11 +3813,10 @@ function StudioSheetHeaderIconButton({
3676
3813
  borderRadius: 100,
3677
3814
  alignItems: "center",
3678
3815
  justifyContent: "center",
3679
- backgroundColor: glassInnerBg,
3680
- opacity: resolvedOpacity
3816
+ backgroundColor: glassBg
3681
3817
  },
3682
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3683
- import_react_native23.Pressable,
3818
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3819
+ import_react_native25.Pressable,
3684
3820
  {
3685
3821
  accessibilityRole: "button",
3686
3822
  accessibilityLabel,
@@ -3698,8 +3834,8 @@ function StudioSheetHeaderIconButton({
3698
3834
  }
3699
3835
  )
3700
3836
  }
3701
- ) : /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3702
- import_react_native23.View,
3837
+ ) : /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3838
+ import_react_native25.View,
3703
3839
  {
3704
3840
  style: {
3705
3841
  width: size,
@@ -3710,8 +3846,8 @@ function StudioSheetHeaderIconButton({
3710
3846
  backgroundColor: solidBg,
3711
3847
  opacity: resolvedOpacity
3712
3848
  },
3713
- children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
3714
- import_react_native23.Pressable,
3849
+ children: /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3850
+ import_react_native25.Pressable,
3715
3851
  {
3716
3852
  accessibilityRole: "button",
3717
3853
  accessibilityLabel,
@@ -3731,15 +3867,15 @@ function StudioSheetHeaderIconButton({
3731
3867
  }
3732
3868
 
3733
3869
  // src/studio/ui/preview-panel/PreviewPanelHeader.tsx
3734
- var import_jsx_runtime22 = require("react/jsx-runtime");
3870
+ var import_jsx_runtime24 = require("react/jsx-runtime");
3735
3871
  function PreviewPanelHeader({ isOwner, onClose, onNavigateHome, onGoToChat }) {
3736
- return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3872
+ return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3737
3873
  StudioSheetHeader,
3738
3874
  {
3739
- 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,
3875
+ left: onNavigateHome ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(StudioSheetHeaderIconButton, { onPress: onNavigateHome, accessibilityLabel: "Home", appearance: "glass", intent: "primary", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(IconHome, { size: 20, colorToken: "onPrimary" }) }) : null,
3740
3876
  center: null,
3741
- right: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(import_react_native24.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
3742
- isOwner ? /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
3877
+ right: /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(import_react_native26.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
3878
+ isOwner ? /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3743
3879
  StudioSheetHeaderIconButton,
3744
3880
  {
3745
3881
  onPress: onGoToChat,
@@ -3747,21 +3883,21 @@ function PreviewPanelHeader({ isOwner, onClose, onNavigateHome, onGoToChat }) {
3747
3883
  intent: "primary",
3748
3884
  appearance: "glass",
3749
3885
  style: { marginRight: 8 },
3750
- children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(IconChat, { size: 20, colorToken: "onPrimary" })
3886
+ children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(IconChat, { size: 20, colorToken: "onPrimary" })
3751
3887
  }
3752
3888
  ) : null,
3753
- /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", appearance: "glass", intent: "primary", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(IconClose, { size: 20, colorToken: "onPrimary" }) })
3889
+ /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", appearance: "glass", intent: "primary", children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(IconClose, { size: 20, colorToken: "onPrimary" }) })
3754
3890
  ] })
3755
3891
  }
3756
3892
  );
3757
3893
  }
3758
3894
 
3759
3895
  // src/components/preview/PreviewHeroCard.tsx
3760
- var import_react_native26 = require("react-native");
3896
+ var import_react_native28 = require("react-native");
3761
3897
 
3762
3898
  // src/components/primitives/Surface.tsx
3763
- var import_react_native25 = require("react-native");
3764
- var import_jsx_runtime23 = require("react/jsx-runtime");
3899
+ var import_react_native27 = require("react-native");
3900
+ var import_jsx_runtime25 = require("react/jsx-runtime");
3765
3901
  function backgroundFor(variant, theme) {
3766
3902
  const { colors } = theme;
3767
3903
  switch (variant) {
@@ -3778,8 +3914,8 @@ function backgroundFor(variant, theme) {
3778
3914
  }
3779
3915
  function Surface({ variant = "surface", border = false, style, ...props }) {
3780
3916
  const theme = useTheme();
3781
- return /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
3782
- import_react_native25.View,
3917
+ return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3918
+ import_react_native27.View,
3783
3919
  {
3784
3920
  ...props,
3785
3921
  style: [
@@ -3792,12 +3928,12 @@ function Surface({ variant = "surface", border = false, style, ...props }) {
3792
3928
  }
3793
3929
 
3794
3930
  // src/components/primitives/Card.tsx
3795
- var import_jsx_runtime24 = require("react/jsx-runtime");
3931
+ var import_jsx_runtime26 = require("react/jsx-runtime");
3796
3932
  function Card({ variant = "surface", padded = true, border = true, style, ...props }) {
3797
3933
  const theme = useTheme();
3798
3934
  const radius = theme.radii.lg;
3799
3935
  const padding = padded ? theme.spacing.lg : 0;
3800
- return /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(
3936
+ return /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
3801
3937
  Surface,
3802
3938
  {
3803
3939
  ...props,
@@ -3809,7 +3945,7 @@ function Card({ variant = "surface", padded = true, border = true, style, ...pro
3809
3945
  }
3810
3946
 
3811
3947
  // src/components/preview/PreviewHeroCard.tsx
3812
- var import_jsx_runtime25 = require("react/jsx-runtime");
3948
+ var import_jsx_runtime27 = require("react/jsx-runtime");
3813
3949
  function PreviewHeroCard({
3814
3950
  aspectRatio = 4 / 3,
3815
3951
  overlayTopLeft,
@@ -3820,7 +3956,7 @@ function PreviewHeroCard({
3820
3956
  }) {
3821
3957
  const theme = useTheme();
3822
3958
  const radius = 16;
3823
- return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
3959
+ return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3824
3960
  Card,
3825
3961
  {
3826
3962
  variant: "surfaceRaised",
@@ -3835,32 +3971,32 @@ function PreviewHeroCard({
3835
3971
  },
3836
3972
  style
3837
3973
  ],
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
3974
+ children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_react_native28.View, { style: { flex: 1 }, children: [
3975
+ background ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_native28.View, { style: { position: "absolute", inset: 0 }, children: background }) : null,
3976
+ image ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_native28.View, { style: { position: "absolute", inset: 0 }, children: image }) : null,
3977
+ overlayTopLeft ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_native28.View, { style: { position: "absolute", top: theme.spacing.sm, left: theme.spacing.sm, zIndex: 2 }, children: overlayTopLeft }) : null,
3978
+ overlayBottom ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(import_react_native28.View, { style: { flex: 1, justifyContent: "flex-end" }, children: overlayBottom }) : null
3843
3979
  ] })
3844
3980
  }
3845
3981
  );
3846
3982
  }
3847
3983
 
3848
3984
  // src/components/preview/PreviewPlaceholder.tsx
3849
- var React23 = __toESM(require("react"));
3850
- var import_react_native27 = require("react-native");
3985
+ var React25 = __toESM(require("react"));
3986
+ var import_react_native29 = require("react-native");
3851
3987
  var import_expo_linear_gradient2 = require("expo-linear-gradient");
3852
- var import_jsx_runtime26 = require("react/jsx-runtime");
3988
+ var import_jsx_runtime28 = require("react/jsx-runtime");
3853
3989
  function PreviewPlaceholder({ visible, style }) {
3854
3990
  if (!visible) return null;
3855
- const opacityAnim = React23.useRef(new import_react_native27.Animated.Value(0)).current;
3856
- React23.useEffect(() => {
3991
+ const opacityAnim = React25.useRef(new import_react_native29.Animated.Value(0)).current;
3992
+ React25.useEffect(() => {
3857
3993
  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 })
3994
+ const animation = import_react_native29.Animated.loop(
3995
+ import_react_native29.Animated.sequence([
3996
+ import_react_native29.Animated.timing(opacityAnim, { toValue: 1, duration: 1500, useNativeDriver: true }),
3997
+ import_react_native29.Animated.timing(opacityAnim, { toValue: 2, duration: 1500, useNativeDriver: true }),
3998
+ import_react_native29.Animated.timing(opacityAnim, { toValue: 3, duration: 1500, useNativeDriver: true }),
3999
+ import_react_native29.Animated.timing(opacityAnim, { toValue: 0, duration: 1500, useNativeDriver: true })
3864
4000
  ])
3865
4001
  );
3866
4002
  animation.start();
@@ -3870,8 +4006,8 @@ function PreviewPlaceholder({ visible, style }) {
3870
4006
  const opacity2 = opacityAnim.interpolate({ inputRange: [0, 1, 2, 3], outputRange: [0, 1, 0, 0] });
3871
4007
  const opacity3 = opacityAnim.interpolate({ inputRange: [0, 1, 2, 3], outputRange: [0, 0, 1, 0] });
3872
4008
  const opacity4 = opacityAnim.interpolate({ inputRange: [0, 1, 2, 3], outputRange: [0, 0, 0, 1] });
3873
- 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)(
4009
+ return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(import_jsx_runtime28.Fragment, { children: [
4010
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native29.Animated.View, { style: [{ position: "absolute", inset: 0, opacity: opacity1 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3875
4011
  import_expo_linear_gradient2.LinearGradient,
3876
4012
  {
3877
4013
  colors: ["rgba(98, 0, 238, 0.45)", "rgba(168, 85, 247, 0.35)"],
@@ -3880,7 +4016,7 @@ function PreviewPlaceholder({ visible, style }) {
3880
4016
  style: { width: "100%", height: "100%" }
3881
4017
  }
3882
4018
  ) }),
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)(
4019
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native29.Animated.View, { style: [{ position: "absolute", inset: 0, opacity: opacity2 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3884
4020
  import_expo_linear_gradient2.LinearGradient,
3885
4021
  {
3886
4022
  colors: ["rgba(168, 85, 247, 0.45)", "rgba(139, 92, 246, 0.35)"],
@@ -3889,7 +4025,7 @@ function PreviewPlaceholder({ visible, style }) {
3889
4025
  style: { width: "100%", height: "100%" }
3890
4026
  }
3891
4027
  ) }),
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)(
4028
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native29.Animated.View, { style: [{ position: "absolute", inset: 0, opacity: opacity3 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3893
4029
  import_expo_linear_gradient2.LinearGradient,
3894
4030
  {
3895
4031
  colors: ["rgba(139, 92, 246, 0.45)", "rgba(126, 34, 206, 0.35)"],
@@ -3898,7 +4034,7 @@ function PreviewPlaceholder({ visible, style }) {
3898
4034
  style: { width: "100%", height: "100%" }
3899
4035
  }
3900
4036
  ) }),
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)(
4037
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native29.Animated.View, { style: [{ position: "absolute", inset: 0, opacity: opacity4 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
3902
4038
  import_expo_linear_gradient2.LinearGradient,
3903
4039
  {
3904
4040
  colors: ["rgba(126, 34, 206, 0.45)", "rgba(98, 0, 238, 0.35)"],
@@ -3911,12 +4047,12 @@ function PreviewPlaceholder({ visible, style }) {
3911
4047
  }
3912
4048
 
3913
4049
  // src/components/preview/PreviewImage.tsx
3914
- var import_react_native28 = require("react-native");
3915
- var import_jsx_runtime27 = require("react/jsx-runtime");
4050
+ var import_react_native30 = require("react-native");
4051
+ var import_jsx_runtime29 = require("react/jsx-runtime");
3916
4052
  function PreviewImage({ uri, onLoad, style }) {
3917
4053
  if (!uri) return null;
3918
- return /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
3919
- import_react_native28.Image,
4054
+ return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
4055
+ import_react_native30.Image,
3920
4056
  {
3921
4057
  source: { uri },
3922
4058
  resizeMode: "cover",
@@ -3927,15 +4063,15 @@ function PreviewImage({ uri, onLoad, style }) {
3927
4063
  }
3928
4064
 
3929
4065
  // src/components/preview/StatsBar.tsx
3930
- var import_react_native29 = require("react-native");
3931
- var import_liquid_glass6 = require("@callstack/liquid-glass");
4066
+ var import_react_native31 = require("react-native");
4067
+ var import_liquid_glass7 = require("@callstack/liquid-glass");
3932
4068
  var import_lucide_react_native5 = require("lucide-react-native");
3933
4069
 
3934
4070
  // src/components/icons/MergeIcon.tsx
3935
4071
  var import_react_native_svg2 = __toESM(require("react-native-svg"));
3936
- var import_jsx_runtime28 = require("react/jsx-runtime");
4072
+ var import_jsx_runtime30 = require("react/jsx-runtime");
3937
4073
  function MergeIcon({ color = "currentColor", width = 24, height = 24, ...props }) {
3938
- return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(import_react_native_svg2.default, { viewBox: "0 0 486 486", width, height, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
4074
+ return /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(import_react_native_svg2.default, { viewBox: "0 0 486 486", width, height, ...props, children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
3939
4075
  import_react_native_svg2.Path,
3940
4076
  {
3941
4077
  d: "M237.025 0H243.664C254.876 95.0361 275.236 175.597 304.743 241.684C334.249 307.478 367.002 357.774 403 392.572L389.722 486C361.691 458.22 338.233 429.417 319.349 399.59C300.464 369.764 284.531 335.843 271.548 297.829C258.565 259.522 246.615 214.343 235.697 162.292L237.91 161.415C228.468 214.928 217.993 261.569 206.485 301.338C194.978 341.107 179.634 375.904 160.455 405.731C141.571 435.265 115.752 462.022 83 486L96.278 392.572C124.014 369.179 147.62 336.72 167.094 295.197C186.864 253.381 202.65 206.886 214.452 155.713C226.255 104.247 233.779 52.343 237.025 0Z",
@@ -3945,7 +4081,7 @@ function MergeIcon({ color = "currentColor", width = 24, height = 24, ...props }
3945
4081
  }
3946
4082
 
3947
4083
  // src/components/preview/StatsBar.tsx
3948
- var import_jsx_runtime29 = require("react/jsx-runtime");
4084
+ var import_jsx_runtime31 = require("react/jsx-runtime");
3949
4085
  function StatsBar({
3950
4086
  likeCount,
3951
4087
  commentCount,
@@ -3959,33 +4095,33 @@ function StatsBar({
3959
4095
  }) {
3960
4096
  const theme = useTheme();
3961
4097
  const statsBgColor = theme.scheme === "dark" ? "rgba(24, 24, 27, 0.5)" : "rgba(255, 255, 255, 0.5)";
3962
- return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3963
- import_react_native29.View,
4098
+ return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
4099
+ import_react_native31.View,
3964
4100
  {
3965
4101
  style: [
3966
4102
  { position: "absolute", bottom: 12, width: "100%", paddingHorizontal: 12 },
3967
4103
  centered && { alignItems: "center" },
3968
4104
  style
3969
4105
  ],
3970
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3971
- import_liquid_glass6.LiquidGlassView,
4106
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
4107
+ ResettableLiquidGlassView,
3972
4108
  {
3973
4109
  style: [
3974
4110
  { borderRadius: 100, overflow: "hidden" },
3975
4111
  fixedWidth ? { width: fixedWidth } : void 0,
3976
- !import_liquid_glass6.isLiquidGlassSupported && { backgroundColor: statsBgColor }
4112
+ !import_liquid_glass7.isLiquidGlassSupported && { backgroundColor: statsBgColor }
3977
4113
  ],
3978
4114
  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: [
3980
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
3981
- import_react_native29.Pressable,
4115
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_react_native31.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", paddingHorizontal: 16 }, children: [
4116
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
4117
+ import_react_native31.Pressable,
3982
4118
  {
3983
4119
  disabled: !onPressLike,
3984
4120
  onPress: onPressLike,
3985
4121
  hitSlop: 8,
3986
4122
  style: { paddingVertical: 8 },
3987
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_react_native29.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
3988
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
4123
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_react_native31.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
4124
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3989
4125
  import_lucide_react_native5.Heart,
3990
4126
  {
3991
4127
  size: 16,
@@ -3994,8 +4130,8 @@ function StatsBar({
3994
4130
  fill: isLiked ? theme.colors.danger : "transparent"
3995
4131
  }
3996
4132
  ),
3997
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(import_react_native29.View, { style: { width: 4 } }),
3998
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
4133
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react_native31.View, { style: { width: 4 } }),
4134
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
3999
4135
  Text,
4000
4136
  {
4001
4137
  variant: "caption",
@@ -4009,24 +4145,24 @@ function StatsBar({
4009
4145
  ] })
4010
4146
  }
4011
4147
  ),
4012
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
4013
- import_react_native29.Pressable,
4148
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
4149
+ import_react_native31.Pressable,
4014
4150
  {
4015
4151
  disabled: !onPressComments,
4016
4152
  onPress: onPressComments,
4017
4153
  hitSlop: 8,
4018
4154
  style: { paddingVertical: 8 },
4019
- children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)(import_react_native29.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
4020
- /* @__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 } }),
4022
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Text, { variant: "caption", style: { color: "#FFFFFF", fontWeight: theme.typography.fontWeight.bold }, children: commentCount })
4155
+ children: /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_react_native31.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
4156
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_lucide_react_native5.MessageCircle, { size: 16, strokeWidth: 2.5, color: "#FFFFFF" }),
4157
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react_native31.View, { style: { width: 4 } }),
4158
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Text, { variant: "caption", style: { color: "#FFFFFF", fontWeight: theme.typography.fontWeight.bold }, children: commentCount })
4023
4159
  ] })
4024
4160
  }
4025
4161
  ),
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 } }),
4029
- /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Text, { variant: "caption", style: { color: "#FFFFFF", fontWeight: theme.typography.fontWeight.bold }, children: forkCount })
4162
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(import_react_native31.View, { style: { flexDirection: "row", alignItems: "center", paddingVertical: 8 }, children: [
4163
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react_native31.View, { style: { transform: [{ scaleY: -1 }] }, children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(MergeIcon, { width: 14, height: 14, color: "#FFFFFF" }) }),
4164
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(import_react_native31.View, { style: { width: 4 } }),
4165
+ /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Text, { variant: "caption", style: { color: "#FFFFFF", fontWeight: theme.typography.fontWeight.bold }, children: forkCount })
4030
4166
  ] })
4031
4167
  ] })
4032
4168
  }
@@ -4036,7 +4172,7 @@ function StatsBar({
4036
4172
  }
4037
4173
 
4038
4174
  // src/components/preview/PreviewStatusBadge.tsx
4039
- var import_react_native30 = require("react-native");
4175
+ var import_react_native32 = require("react-native");
4040
4176
  var import_lucide_react_native6 = require("lucide-react-native");
4041
4177
 
4042
4178
  // src/data/apps/types.ts
@@ -4051,7 +4187,7 @@ var APP_STATUS_LABEL = {
4051
4187
  };
4052
4188
 
4053
4189
  // src/components/preview/PreviewStatusBadge.tsx
4054
- var import_jsx_runtime30 = require("react/jsx-runtime");
4190
+ var import_jsx_runtime32 = require("react/jsx-runtime");
4055
4191
  var STATUS_BG = {
4056
4192
  ready: "#10B981",
4057
4193
  // emerald-500
@@ -4080,8 +4216,8 @@ var STATUS_ICON = {
4080
4216
  function PreviewStatusBadge({ status }) {
4081
4217
  const IconComp = STATUS_ICON[status];
4082
4218
  const label = APP_STATUS_LABEL[status] ?? status;
4083
- return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(
4084
- import_react_native30.View,
4219
+ return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(
4220
+ import_react_native32.View,
4085
4221
  {
4086
4222
  style: {
4087
4223
  flexDirection: "row",
@@ -4092,15 +4228,15 @@ function PreviewStatusBadge({ status }) {
4092
4228
  backgroundColor: STATUS_BG[status]
4093
4229
  },
4094
4230
  children: [
4095
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(IconComp, { size: 12, color: "#FFFFFF", style: { marginRight: 4 } }),
4096
- /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Text, { style: { color: "#FFFFFF", fontSize: 11, lineHeight: 14 }, children: label })
4231
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(IconComp, { size: 12, color: "#FFFFFF", style: { marginRight: 4 } }),
4232
+ /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Text, { style: { color: "#FFFFFF", fontSize: 11, lineHeight: 14 }, children: label })
4097
4233
  ]
4098
4234
  }
4099
4235
  );
4100
4236
  }
4101
4237
 
4102
4238
  // src/studio/ui/preview-panel/PreviewHeroSection.tsx
4103
- var import_jsx_runtime31 = require("react/jsx-runtime");
4239
+ var import_jsx_runtime33 = require("react/jsx-runtime");
4104
4240
  function PreviewHeroSection({
4105
4241
  appStatus,
4106
4242
  showProcessing,
@@ -4109,13 +4245,13 @@ function PreviewHeroSection({
4109
4245
  onImageLoad,
4110
4246
  stats
4111
4247
  }) {
4112
- return /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
4248
+ return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4113
4249
  PreviewHeroCard,
4114
4250
  {
4115
- overlayTopLeft: showProcessing ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(PreviewStatusBadge, { status: appStatus }) : null,
4116
- background: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(PreviewPlaceholder, { visible: !imageLoaded }),
4117
- image: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(PreviewImage, { uri: imageUrl, onLoad: onImageLoad }),
4118
- overlayBottom: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(
4251
+ overlayTopLeft: showProcessing ? /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(PreviewStatusBadge, { status: appStatus }) : null,
4252
+ background: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(PreviewPlaceholder, { visible: !imageLoaded }),
4253
+ image: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(PreviewImage, { uri: imageUrl, onLoad: onImageLoad }),
4254
+ overlayBottom: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4119
4255
  StatsBar,
4120
4256
  {
4121
4257
  likeCount: stats.likeCount,
@@ -4134,11 +4270,11 @@ function PreviewHeroSection({
4134
4270
  }
4135
4271
 
4136
4272
  // src/studio/ui/preview-panel/PreviewMetaSection.tsx
4137
- var import_react_native32 = require("react-native");
4273
+ var import_react_native34 = require("react-native");
4138
4274
 
4139
4275
  // src/components/preview/PreviewMetaRow.tsx
4140
- var import_react_native31 = require("react-native");
4141
- var import_jsx_runtime32 = require("react/jsx-runtime");
4276
+ var import_react_native33 = require("react-native");
4277
+ var import_jsx_runtime34 = require("react/jsx-runtime");
4142
4278
  function PreviewMetaRow({
4143
4279
  avatarUri,
4144
4280
  creatorName,
@@ -4149,11 +4285,11 @@ function PreviewMetaRow({
4149
4285
  style
4150
4286
  }) {
4151
4287
  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: [
4154
- /* @__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: [
4156
- /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4288
+ return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react_native33.View, { style: [{ alignSelf: "stretch" }, style], children: [
4289
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react_native33.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
4290
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Avatar, { uri: avatarUri, name: creatorName, size: 24, style: { marginRight: theme.spacing.sm } }),
4291
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react_native33.View, { style: { flexDirection: "row", alignItems: "center", flex: 1, minWidth: 0, marginRight: theme.spacing.sm }, children: [
4292
+ /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4157
4293
  Text,
4158
4294
  {
4159
4295
  numberOfLines: 1,
@@ -4167,11 +4303,11 @@ function PreviewMetaRow({
4167
4303
  children: title
4168
4304
  }
4169
4305
  ),
4170
- tag ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react_native31.View, { style: { marginLeft: theme.spacing.sm }, children: tag }) : null
4306
+ tag ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_native33.View, { style: { marginLeft: theme.spacing.sm }, children: tag }) : null
4171
4307
  ] }),
4172
- rightMetric ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(import_react_native31.View, { children: rightMetric }) : null
4308
+ rightMetric ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_native33.View, { children: rightMetric }) : null
4173
4309
  ] }),
4174
- subtitle ? /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(
4310
+ subtitle ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4175
4311
  Text,
4176
4312
  {
4177
4313
  numberOfLines: 2,
@@ -4214,20 +4350,20 @@ function statusDescription(status, statusError) {
4214
4350
  }
4215
4351
 
4216
4352
  // src/studio/ui/preview-panel/PreviewMetaSection.tsx
4217
- var import_jsx_runtime33 = require("react/jsx-runtime");
4353
+ var import_jsx_runtime35 = require("react/jsx-runtime");
4218
4354
  function PreviewMetaSection({ app, isOwner, creator, downloadsCount }) {
4219
4355
  var _a;
4220
4356
  const theme = useTheme();
4221
- return /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4357
+ return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4222
4358
  PreviewMetaRow,
4223
4359
  {
4224
4360
  title: app.name,
4225
4361
  subtitle: app.description,
4226
4362
  avatarUri: (creator == null ? void 0 : creator.avatar) ?? null,
4227
4363
  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,
4229
- rightMetric: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(
4230
- import_react_native32.View,
4364
+ tag: isOwner || app.forkedFromAppId ? /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_react_native34.View, { style: { paddingHorizontal: 8, paddingVertical: 2, borderRadius: 999, backgroundColor: "#3700B3" }, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Text, { variant: "caption", style: { color: "#fff", fontWeight: theme.typography.fontWeight.semibold }, children: app.forkedFromAppId ? "Remix" : "Owner" }) }) : null,
4365
+ rightMetric: /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
4366
+ import_react_native34.View,
4231
4367
  {
4232
4368
  style: {
4233
4369
  flexDirection: "row",
@@ -4238,7 +4374,7 @@ function PreviewMetaSection({ app, isOwner, creator, downloadsCount }) {
4238
4374
  backgroundColor: withAlpha(theme.colors.neutral, 0.3)
4239
4375
  },
4240
4376
  children: [
4241
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(
4377
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4242
4378
  Text,
4243
4379
  {
4244
4380
  style: {
@@ -4251,7 +4387,7 @@ function PreviewMetaSection({ app, isOwner, creator, downloadsCount }) {
4251
4387
  children: formatCount(downloadsCount ?? ((_a = app.insights) == null ? void 0 : _a.totalDownloads) ?? 0)
4252
4388
  }
4253
4389
  ),
4254
- /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(IconPlay, { size: 14, colorToken: "textMuted", fill: theme.colors.textMuted })
4390
+ /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(IconPlay, { size: 14, colorToken: "textMuted", fill: theme.colors.textMuted })
4255
4391
  ]
4256
4392
  }
4257
4393
  ),
@@ -4261,11 +4397,11 @@ function PreviewMetaSection({ app, isOwner, creator, downloadsCount }) {
4261
4397
  }
4262
4398
 
4263
4399
  // src/studio/ui/preview-panel/PreviewCustomizeSection.tsx
4264
- var import_react_native34 = require("react-native");
4400
+ var import_react_native36 = require("react-native");
4265
4401
 
4266
4402
  // src/studio/ui/preview-panel/PressableCardRow.tsx
4267
- var import_react_native33 = require("react-native");
4268
- var import_jsx_runtime34 = require("react/jsx-runtime");
4403
+ var import_react_native35 = require("react-native");
4404
+ var import_jsx_runtime36 = require("react/jsx-runtime");
4269
4405
  function PressableCardRow({
4270
4406
  accessibilityLabel,
4271
4407
  onPress,
@@ -4276,31 +4412,31 @@ function PressableCardRow({
4276
4412
  right,
4277
4413
  style
4278
4414
  }) {
4279
- return /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
4280
- import_react_native33.Pressable,
4415
+ return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4416
+ import_react_native35.Pressable,
4281
4417
  {
4282
4418
  accessibilityRole: "button",
4283
4419
  accessibilityLabel,
4284
4420
  disabled,
4285
4421
  onPress,
4286
4422
  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: [
4423
+ children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Card, { padded: false, border: false, style, children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_react_native35.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
4288
4424
  left,
4289
- /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_react_native33.View, { style: { flex: 1, minWidth: 0 }, children: [
4425
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_react_native35.View, { style: { flex: 1, minWidth: 0 }, children: [
4290
4426
  title,
4291
4427
  subtitle ? subtitle : null
4292
4428
  ] }),
4293
- right ? /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_react_native33.View, { style: { marginLeft: 16 }, children: right }) : null
4429
+ right ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_native35.View, { style: { marginLeft: 16 }, children: right }) : null
4294
4430
  ] }) })
4295
4431
  }
4296
4432
  );
4297
4433
  }
4298
4434
 
4299
4435
  // src/studio/ui/preview-panel/SectionTitle.tsx
4300
- var import_jsx_runtime35 = require("react/jsx-runtime");
4436
+ var import_jsx_runtime37 = require("react/jsx-runtime");
4301
4437
  function SectionTitle({ children, marginTop }) {
4302
4438
  const theme = useTheme();
4303
- return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
4439
+ return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
4304
4440
  Text,
4305
4441
  {
4306
4442
  style: {
@@ -4319,7 +4455,7 @@ function SectionTitle({ children, marginTop }) {
4319
4455
  }
4320
4456
 
4321
4457
  // src/studio/ui/preview-panel/PreviewCustomizeSection.tsx
4322
- var import_jsx_runtime36 = require("react/jsx-runtime");
4458
+ var import_jsx_runtime38 = require("react/jsx-runtime");
4323
4459
  function PreviewCustomizeSection({
4324
4460
  app,
4325
4461
  isOwner,
@@ -4329,10 +4465,10 @@ function PreviewCustomizeSection({
4329
4465
  onStartDraw
4330
4466
  }) {
4331
4467
  const theme = useTheme();
4332
- return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_jsx_runtime36.Fragment, { children: [
4333
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(SectionTitle, { children: "Customize" }),
4334
- showProcessing ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
4335
- import_react_native34.View,
4468
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_jsx_runtime38.Fragment, { children: [
4469
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(SectionTitle, { children: "Customize" }),
4470
+ showProcessing ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
4471
+ import_react_native36.View,
4336
4472
  {
4337
4473
  style: {
4338
4474
  flexDirection: "row",
@@ -4345,8 +4481,8 @@ function PreviewCustomizeSection({
4345
4481
  marginBottom: theme.spacing.sm
4346
4482
  },
4347
4483
  children: [
4348
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4349
- import_react_native34.View,
4484
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4485
+ import_react_native36.View,
4350
4486
  {
4351
4487
  style: {
4352
4488
  width: 40,
@@ -4357,17 +4493,17 @@ function PreviewCustomizeSection({
4357
4493
  backgroundColor: withAlpha(theme.colors.warning, 0.1),
4358
4494
  marginRight: theme.spacing.lg
4359
4495
  },
4360
- children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_native34.ActivityIndicator, { color: theme.colors.warning, size: "small" })
4496
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_react_native36.ActivityIndicator, { color: theme.colors.warning, size: "small" })
4361
4497
  }
4362
4498
  ),
4363
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_react_native34.View, { style: { flex: 1, minWidth: 0 }, children: [
4364
- /* @__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
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Text, { style: { color: theme.colors.textMuted, fontSize: 12, lineHeight: 16, marginTop: 2 }, children: statusDescription(app.status, app.statusError) })
4499
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react_native36.View, { style: { flex: 1, minWidth: 0 }, children: [
4500
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { style: { color: theme.colors.text, fontSize: 16, lineHeight: 20, fontWeight: theme.typography.fontWeight.semibold }, children: app.status === "error" ? "Error" : "Processing" }),
4501
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { style: { color: theme.colors.textMuted, fontSize: 12, lineHeight: 16, marginTop: 2 }, children: statusDescription(app.status, app.statusError) })
4366
4502
  ] })
4367
4503
  ]
4368
4504
  }
4369
4505
  ) : null,
4370
- /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4506
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4371
4507
  PressableCardRow,
4372
4508
  {
4373
4509
  accessibilityLabel: isOwner ? "Edit app" : "Remix app",
@@ -4380,8 +4516,8 @@ function PreviewCustomizeSection({
4380
4516
  borderColor: withAlpha(theme.colors.primary, 0.1),
4381
4517
  marginBottom: theme.spacing.sm
4382
4518
  },
4383
- left: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4384
- import_react_native34.View,
4519
+ left: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4520
+ import_react_native36.View,
4385
4521
  {
4386
4522
  style: {
4387
4523
  width: 40,
@@ -4392,15 +4528,15 @@ function PreviewCustomizeSection({
4392
4528
  backgroundColor: withAlpha(theme.colors.primary, 0.1),
4393
4529
  marginRight: theme.spacing.lg
4394
4530
  },
4395
- children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(IconChat, { size: 20, colorToken: "primary" })
4531
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(IconChat, { size: 20, colorToken: "primary" })
4396
4532
  }
4397
4533
  ),
4398
- title: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Text, { style: { color: theme.colors.text, fontSize: 16, lineHeight: 20, fontWeight: theme.typography.fontWeight.semibold }, children: isOwner ? app.forkedFromAppId ? "Edit your Remix" : "Edit Your App" : "Remix App" }),
4399
- subtitle: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Text, { style: { color: theme.colors.textMuted, fontSize: 12, lineHeight: 16, marginTop: 2 }, children: isOwner && app.forkedFromAppId ? "Make changes to your remix with chat" : shouldForkOnEdit ? "Chat to create your own copy and edit it" : "Chat to apply changes" }),
4400
- right: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(IconChevronRight, { size: 20, colorToken: "textMuted" })
4534
+ title: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { style: { color: theme.colors.text, fontSize: 16, lineHeight: 20, fontWeight: theme.typography.fontWeight.semibold }, children: isOwner ? app.forkedFromAppId ? "Edit your Remix" : "Edit Your App" : "Remix App" }),
4535
+ subtitle: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { style: { color: theme.colors.textMuted, fontSize: 12, lineHeight: 16, marginTop: 2 }, children: isOwner && app.forkedFromAppId ? "Make changes to your remix with chat" : shouldForkOnEdit ? "Chat to create your own copy and edit it" : "Chat to apply changes" }),
4536
+ right: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(IconChevronRight, { size: 20, colorToken: "textMuted" })
4401
4537
  }
4402
4538
  ),
4403
- isOwner && onStartDraw ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4539
+ isOwner && onStartDraw ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4404
4540
  PressableCardRow,
4405
4541
  {
4406
4542
  accessibilityLabel: "Draw changes",
@@ -4413,8 +4549,8 @@ function PreviewCustomizeSection({
4413
4549
  borderColor: withAlpha(theme.colors.danger, 0.1),
4414
4550
  marginBottom: theme.spacing.sm
4415
4551
  },
4416
- left: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
4417
- import_react_native34.View,
4552
+ left: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4553
+ import_react_native36.View,
4418
4554
  {
4419
4555
  style: {
4420
4556
  width: 40,
@@ -4425,31 +4561,31 @@ function PreviewCustomizeSection({
4425
4561
  backgroundColor: withAlpha(theme.colors.danger, 0.1),
4426
4562
  marginRight: theme.spacing.lg
4427
4563
  },
4428
- children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(IconDraw, { size: 20, colorToken: "danger" })
4564
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(IconDraw, { size: 20, colorToken: "danger" })
4429
4565
  }
4430
4566
  ),
4431
- title: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Text, { style: { color: theme.colors.text, fontSize: 16, lineHeight: 20, fontWeight: theme.typography.fontWeight.semibold }, children: "Draw Changes" }),
4432
- subtitle: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Text, { style: { color: theme.colors.textMuted, fontSize: 12, lineHeight: 16, marginTop: 2 }, children: "Annotate the app with drawings" }),
4433
- right: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(IconChevronRight, { size: 20, colorToken: "textMuted" })
4567
+ title: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { style: { color: theme.colors.text, fontSize: 16, lineHeight: 20, fontWeight: theme.typography.fontWeight.semibold }, children: "Draw Changes" }),
4568
+ subtitle: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { style: { color: theme.colors.textMuted, fontSize: 12, lineHeight: 16, marginTop: 2 }, children: "Annotate the app with drawings" }),
4569
+ right: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(IconChevronRight, { size: 20, colorToken: "textMuted" })
4434
4570
  }
4435
4571
  ) : null
4436
4572
  ] });
4437
4573
  }
4438
4574
 
4439
4575
  // src/studio/ui/preview-panel/PreviewCollaborateSection.tsx
4440
- var React29 = __toESM(require("react"));
4441
- var import_react_native40 = require("react-native");
4576
+ var React31 = __toESM(require("react"));
4577
+ var import_react_native42 = require("react-native");
4442
4578
  var import_lucide_react_native9 = require("lucide-react-native");
4443
4579
 
4444
4580
  // src/components/merge-requests/MergeRequestStatusCard.tsx
4445
- var React25 = __toESM(require("react"));
4446
- var import_react_native36 = require("react-native");
4581
+ var React27 = __toESM(require("react"));
4582
+ var import_react_native38 = require("react-native");
4447
4583
  var import_lucide_react_native7 = require("lucide-react-native");
4448
4584
 
4449
4585
  // src/components/primitives/MarkdownText.tsx
4450
- var import_react_native35 = require("react-native");
4586
+ var import_react_native37 = require("react-native");
4451
4587
  var import_react_native_markdown_display = __toESM(require("react-native-markdown-display"));
4452
- var import_jsx_runtime37 = require("react/jsx-runtime");
4588
+ var import_jsx_runtime39 = require("react/jsx-runtime");
4453
4589
  function MarkdownText({ markdown, variant = "chat", bodyColor, style }) {
4454
4590
  const theme = useTheme();
4455
4591
  const isDark = theme.scheme === "dark";
@@ -4460,7 +4596,7 @@ function MarkdownText({ markdown, variant = "chat", bodyColor, style }) {
4460
4596
  const codeTextColor = isDark ? "#FFFFFF" : theme.colors.text;
4461
4597
  const paragraphBottom = variant === "mergeRequest" ? 8 : 6;
4462
4598
  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)(
4599
+ return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(import_react_native37.View, { style, children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4464
4600
  import_react_native_markdown_display.default,
4465
4601
  {
4466
4602
  style: {
@@ -4473,7 +4609,7 @@ function MarkdownText({ markdown, variant = "chat", bodyColor, style }) {
4473
4609
  paddingHorizontal: variant === "mergeRequest" ? 6 : 4,
4474
4610
  paddingVertical: variant === "mergeRequest" ? 2 : 0,
4475
4611
  borderRadius: variant === "mergeRequest" ? 6 : 4,
4476
- fontFamily: import_react_native35.Platform.OS === "ios" ? "Menlo" : "monospace",
4612
+ fontFamily: import_react_native37.Platform.OS === "ios" ? "Menlo" : "monospace",
4477
4613
  fontSize: 13
4478
4614
  },
4479
4615
  code_block: {
@@ -4524,11 +4660,11 @@ function toIsoString(input) {
4524
4660
  }
4525
4661
 
4526
4662
  // src/components/merge-requests/useControlledExpansion.ts
4527
- var React24 = __toESM(require("react"));
4663
+ var React26 = __toESM(require("react"));
4528
4664
  function useControlledExpansion(props) {
4529
- const [uncontrolled, setUncontrolled] = React24.useState(false);
4665
+ const [uncontrolled, setUncontrolled] = React26.useState(false);
4530
4666
  const expanded = props.expanded ?? uncontrolled;
4531
- const setExpanded = React24.useCallback(
4667
+ const setExpanded = React26.useCallback(
4532
4668
  (next) => {
4533
4669
  var _a;
4534
4670
  (_a = props.onExpandedChange) == null ? void 0 : _a.call(props, next);
@@ -4540,7 +4676,7 @@ function useControlledExpansion(props) {
4540
4676
  }
4541
4677
 
4542
4678
  // src/components/merge-requests/MergeRequestStatusCard.tsx
4543
- var import_jsx_runtime38 = require("react/jsx-runtime");
4679
+ var import_jsx_runtime40 = require("react/jsx-runtime");
4544
4680
  function MergeRequestStatusCard({
4545
4681
  mergeRequest,
4546
4682
  expanded: expandedProp,
@@ -4553,8 +4689,8 @@ function MergeRequestStatusCard({
4553
4689
  const isDark = theme.scheme === "dark";
4554
4690
  const textColor = isDark ? "#FFFFFF" : "#000000";
4555
4691
  const subTextColor = isDark ? "#A1A1AA" : "#71717A";
4556
- const status = React25.useMemo(() => getMergeRequestStatusDisplay(String(mergeRequest.status)), [mergeRequest.status]);
4557
- const { StatusIcon, iconColor, bgColor, statusText } = React25.useMemo(() => {
4692
+ const status = React27.useMemo(() => getMergeRequestStatusDisplay(String(mergeRequest.status)), [mergeRequest.status]);
4693
+ const { StatusIcon, iconColor, bgColor, statusText } = React27.useMemo(() => {
4558
4694
  switch (mergeRequest.status) {
4559
4695
  case "approved":
4560
4696
  case "merged":
@@ -4585,15 +4721,15 @@ function MergeRequestStatusCard({
4585
4721
  const createdIso = toIsoString(mergeRequest.createdAt ?? null);
4586
4722
  const headerTimeAgo = updatedIso ? formatTimeAgo(updatedIso) : "";
4587
4723
  const createdTimeAgo = createdIso ? formatTimeAgo(createdIso) : "";
4588
- const rotate = React25.useRef(new import_react_native36.Animated.Value(expanded ? 1 : 0)).current;
4589
- React25.useEffect(() => {
4590
- import_react_native36.Animated.timing(rotate, {
4724
+ const rotate = React27.useRef(new import_react_native38.Animated.Value(expanded ? 1 : 0)).current;
4725
+ React27.useEffect(() => {
4726
+ import_react_native38.Animated.timing(rotate, {
4591
4727
  toValue: expanded ? 1 : 0,
4592
4728
  duration: 200,
4593
4729
  useNativeDriver: true
4594
4730
  }).start();
4595
4731
  }, [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)(
4732
+ return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_native38.Pressable, { onPress: () => setExpanded(!expanded), style: ({ pressed }) => [{ opacity: pressed ? 0.95 : 1 }], children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
4597
4733
  Card,
4598
4734
  {
4599
4735
  padded: false,
@@ -4606,11 +4742,11 @@ function MergeRequestStatusCard({
4606
4742
  style
4607
4743
  ],
4608
4744
  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: [
4613
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4745
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", gap: theme.spacing.lg }, children: [
4746
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_native38.View, { style: { width: 40, height: 40, borderRadius: 999, alignItems: "center", justifyContent: "center", backgroundColor: bgColor }, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(StatusIcon, { size: 20, color: iconColor }) }),
4747
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flex: 1, minWidth: 0 }, children: [
4748
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, children: [
4749
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4614
4750
  Text,
4615
4751
  {
4616
4752
  style: {
@@ -4624,12 +4760,12 @@ function MergeRequestStatusCard({
4624
4760
  children: statusText
4625
4761
  }
4626
4762
  ),
4627
- headerTimeAgo ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { style: { fontSize: 10, lineHeight: 14, marginLeft: theme.spacing.sm, color: withAlpha(theme.colors.textMuted, 0.6) }, children: headerTimeAgo }) : null
4763
+ headerTimeAgo ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 10, lineHeight: 14, marginLeft: theme.spacing.sm, color: withAlpha(theme.colors.textMuted, 0.6) }, children: headerTimeAgo }) : null
4628
4764
  ] }),
4629
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { style: { fontSize: 12, lineHeight: 16, color: theme.colors.textMuted }, numberOfLines: 1, children: mergeRequest.title ?? "Untitled merge request" })
4765
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 12, lineHeight: 16, color: theme.colors.textMuted }, numberOfLines: 1, children: mergeRequest.title ?? "Untitled merge request" })
4630
4766
  ] }),
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,
4767
+ headerRight ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_react_native38.View, { children: headerRight }) : /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4768
+ import_react_native38.Animated.View,
4633
4769
  {
4634
4770
  style: {
4635
4771
  transform: [
@@ -4638,12 +4774,12 @@ function MergeRequestStatusCard({
4638
4774
  }
4639
4775
  ]
4640
4776
  },
4641
- children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(import_lucide_react_native7.ChevronDown, { size: 20, color: withAlpha(theme.colors.textMuted, 0.4) })
4777
+ children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react_native7.ChevronDown, { size: 20, color: withAlpha(theme.colors.textMuted, 0.4) })
4642
4778
  }
4643
4779
  )
4644
4780
  ] }),
4645
- expanded ? /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(import_react_native36.View, { style: { marginTop: 16, marginLeft: 56 }, children: [
4646
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4781
+ expanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { marginTop: 16, marginLeft: 56 }, children: [
4782
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4647
4783
  Text,
4648
4784
  {
4649
4785
  style: {
@@ -4657,7 +4793,7 @@ function MergeRequestStatusCard({
4657
4793
  children: status.text
4658
4794
  }
4659
4795
  ),
4660
- createdTimeAgo ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
4796
+ createdTimeAgo ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4661
4797
  Text,
4662
4798
  {
4663
4799
  style: {
@@ -4668,8 +4804,8 @@ function MergeRequestStatusCard({
4668
4804
  children: createdTimeAgo
4669
4805
  }
4670
4806
  ) : null,
4671
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { style: { fontSize: 16, fontWeight: "600", color: textColor, marginBottom: 8 }, children: mergeRequest.title ?? "Untitled merge request" }),
4672
- mergeRequest.description ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(MarkdownText, { markdown: mergeRequest.description, variant: "mergeRequest" }) : null
4807
+ /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 16, fontWeight: "600", color: textColor, marginBottom: 8 }, children: mergeRequest.title ?? "Untitled merge request" }),
4808
+ mergeRequest.description ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(MarkdownText, { markdown: mergeRequest.description, variant: "mergeRequest" }) : null
4673
4809
  ] }) : null
4674
4810
  ]
4675
4811
  }
@@ -4677,18 +4813,18 @@ function MergeRequestStatusCard({
4677
4813
  }
4678
4814
 
4679
4815
  // src/components/merge-requests/ReviewMergeRequestCarousel.tsx
4680
- var React28 = __toESM(require("react"));
4681
- var import_react_native39 = require("react-native");
4816
+ var React30 = __toESM(require("react"));
4817
+ var import_react_native41 = require("react-native");
4682
4818
 
4683
4819
  // src/components/merge-requests/ReviewMergeRequestCard.tsx
4684
- var React27 = __toESM(require("react"));
4685
- var import_react_native38 = require("react-native");
4820
+ var React29 = __toESM(require("react"));
4821
+ var import_react_native40 = require("react-native");
4686
4822
  var import_lucide_react_native8 = require("lucide-react-native");
4687
4823
 
4688
4824
  // src/components/merge-requests/ReviewMergeRequestActionButton.tsx
4689
- var React26 = __toESM(require("react"));
4690
- var import_react_native37 = require("react-native");
4691
- var import_jsx_runtime39 = require("react/jsx-runtime");
4825
+ var React28 = __toESM(require("react"));
4826
+ var import_react_native39 = require("react-native");
4827
+ var import_jsx_runtime41 = require("react/jsx-runtime");
4692
4828
  function ReviewMergeRequestActionButton({
4693
4829
  accessibilityLabel,
4694
4830
  backgroundColor,
@@ -4697,14 +4833,14 @@ function ReviewMergeRequestActionButton({
4697
4833
  children,
4698
4834
  iconOnly
4699
4835
  }) {
4700
- const [pressed, setPressed] = React26.useState(false);
4836
+ const [pressed, setPressed] = React28.useState(false);
4701
4837
  const height = iconOnly ? 36 : 40;
4702
4838
  const width = iconOnly ? 36 : void 0;
4703
4839
  const paddingHorizontal = iconOnly ? 0 : 16;
4704
4840
  const paddingVertical = iconOnly ? 0 : 8;
4705
4841
  const opacity = disabled ? 0.5 : pressed ? 0.9 : 1;
4706
- return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4707
- import_react_native37.View,
4842
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
4843
+ import_react_native39.View,
4708
4844
  {
4709
4845
  style: {
4710
4846
  width,
@@ -4718,8 +4854,8 @@ function ReviewMergeRequestActionButton({
4718
4854
  paddingVertical,
4719
4855
  justifyContent: "center"
4720
4856
  },
4721
- children: /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
4722
- import_react_native37.Pressable,
4857
+ children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
4858
+ import_react_native39.Pressable,
4723
4859
  {
4724
4860
  accessibilityRole: "button",
4725
4861
  accessibilityLabel,
@@ -4742,7 +4878,7 @@ function ReviewMergeRequestActionButton({
4742
4878
  }
4743
4879
 
4744
4880
  // src/components/merge-requests/ReviewMergeRequestCard.tsx
4745
- var import_jsx_runtime40 = require("react/jsx-runtime");
4881
+ var import_jsx_runtime42 = require("react/jsx-runtime");
4746
4882
  function ReviewMergeRequestCard({
4747
4883
  mr,
4748
4884
  index,
@@ -4759,14 +4895,14 @@ function ReviewMergeRequestCard({
4759
4895
  onTest
4760
4896
  }) {
4761
4897
  const theme = useTheme();
4762
- const status = React27.useMemo(() => getMergeRequestStatusDisplay(mr.status), [mr.status]);
4898
+ const status = React29.useMemo(() => getMergeRequestStatusDisplay(mr.status), [mr.status]);
4763
4899
  const canAct = mr.status === "open";
4764
- const rotate = React27.useRef(new import_react_native38.Animated.Value(isExpanded ? 1 : 0)).current;
4765
- React27.useEffect(() => {
4766
- import_react_native38.Animated.timing(rotate, { toValue: isExpanded ? 1 : 0, duration: 200, useNativeDriver: true }).start();
4900
+ const rotate = React29.useRef(new import_react_native40.Animated.Value(isExpanded ? 1 : 0)).current;
4901
+ React29.useEffect(() => {
4902
+ import_react_native40.Animated.timing(rotate, { toValue: isExpanded ? 1 : 0, duration: 200, useNativeDriver: true }).start();
4767
4903
  }, [isExpanded, rotate]);
4768
4904
  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)(
4905
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_native40.Pressable, { onPress: onToggle, style: ({ pressed }) => ({ opacity: pressed ? 0.95 : 1 }), children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
4770
4906
  Card,
4771
4907
  {
4772
4908
  padded: false,
@@ -4779,10 +4915,10 @@ function ReviewMergeRequestCard({
4779
4915
  }
4780
4916
  ],
4781
4917
  children: [
4782
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", gap: 12 }, children: [
4783
- /* @__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: [
4785
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4918
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react_native40.View, { style: { flexDirection: "row", alignItems: "center", gap: 12 }, children: [
4919
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Avatar, { size: 40, uri: (creator == null ? void 0 : creator.avatar) ?? null, name: (creator == null ? void 0 : creator.name) ?? void 0 }),
4920
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react_native40.View, { style: { flex: 1, minWidth: 0 }, children: [
4921
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4786
4922
  Text,
4787
4923
  {
4788
4924
  style: { fontWeight: theme.typography.fontWeight.semibold, color: theme.colors.text, fontSize: 16, lineHeight: 20 },
@@ -4790,24 +4926,24 @@ function ReviewMergeRequestCard({
4790
4926
  children: mr.title ?? "Untitled merge request"
4791
4927
  }
4792
4928
  ),
4793
- /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Text, { style: { color: theme.colors.textMuted, fontSize: 12, lineHeight: 16 }, numberOfLines: 1, children: [
4929
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(Text, { style: { color: theme.colors.textMuted, fontSize: 12, lineHeight: 16 }, numberOfLines: 1, children: [
4794
4930
  (creator == null ? void 0 : creator.name) ?? "Loading...",
4795
4931
  " \xB7 ",
4796
4932
  position
4797
4933
  ] })
4798
4934
  ] }),
4799
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4800
- import_react_native38.Animated.View,
4935
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4936
+ import_react_native40.Animated.View,
4801
4937
  {
4802
4938
  style: {
4803
4939
  transform: [{ rotate: rotate.interpolate({ inputRange: [0, 1], outputRange: ["0deg", "180deg"] }) }]
4804
4940
  },
4805
- children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react_native8.ChevronDown, { size: 20, color: withAlpha(theme.colors.textMuted, 0.4) })
4941
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react_native8.ChevronDown, { size: 20, color: withAlpha(theme.colors.textMuted, 0.4) })
4806
4942
  }
4807
4943
  )
4808
4944
  ] }),
4809
- isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { marginTop: 16 }, children: [
4810
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4945
+ isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react_native40.View, { style: { marginTop: 16 }, children: [
4946
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4811
4947
  Text,
4812
4948
  {
4813
4949
  style: {
@@ -4821,13 +4957,13 @@ function ReviewMergeRequestCard({
4821
4957
  children: status.text
4822
4958
  }
4823
4959
  ),
4824
- /* @__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
- mr.description ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(MarkdownText, { markdown: mr.description, variant: "mergeRequest" }) : null
4960
+ /* @__PURE__ */ (0, import_jsx_runtime42.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..." }),
4961
+ mr.description ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(MarkdownText, { markdown: mr.description, variant: "mergeRequest" }) : null
4826
4962
  ] }) : 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: [
4830
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4963
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_native40.View, { style: { height: 1, backgroundColor: withAlpha(theme.colors.borderStrong, 0.5), marginTop: 12, marginBottom: 12 } }),
4964
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react_native40.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between" }, children: [
4965
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react_native40.View, { style: { flexDirection: "row", gap: 8 }, children: [
4966
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4831
4967
  ReviewMergeRequestActionButton,
4832
4968
  {
4833
4969
  accessibilityLabel: "Reject",
@@ -4835,13 +4971,13 @@ function ReviewMergeRequestCard({
4835
4971
  disabled: !canAct || isAnyProcessing,
4836
4972
  onPress: onReject,
4837
4973
  iconOnly: !isExpanded,
4838
- children: /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(import_react_native38.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
4839
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react_native8.X, { size: 18, color: "#FFFFFF" }),
4840
- isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 13, color: "#FFFFFF", fontWeight: theme.typography.fontWeight.semibold }, children: "Reject" }) : null
4974
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react_native40.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
4975
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react_native8.X, { size: 18, color: "#FFFFFF" }),
4976
+ isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Text, { style: { fontSize: 13, color: "#FFFFFF", fontWeight: theme.typography.fontWeight.semibold }, children: "Reject" }) : null
4841
4977
  ] })
4842
4978
  }
4843
4979
  ),
4844
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4980
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4845
4981
  ReviewMergeRequestActionButton,
4846
4982
  {
4847
4983
  accessibilityLabel: !canAct ? "Not actionable" : isProcessing ? "Processing" : "Approve",
@@ -4849,17 +4985,17 @@ function ReviewMergeRequestCard({
4849
4985
  disabled: !canAct || isAnyProcessing,
4850
4986
  onPress: onApprove,
4851
4987
  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" }),
4854
- 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: [
4856
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react_native8.Check, { size: 18, color: "#FFFFFF" }),
4857
- isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 13, color: "#FFFFFF", fontWeight: theme.typography.fontWeight.semibold }, children: "Approve" }) : null
4988
+ children: isProcessing ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react_native40.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
4989
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_native40.ActivityIndicator, { size: "small", color: "#FFFFFF" }),
4990
+ isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Text, { style: { fontSize: 13, color: "#FFFFFF", fontWeight: theme.typography.fontWeight.semibold }, children: "Processing" }) : null
4991
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react_native40.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
4992
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react_native8.Check, { size: 18, color: "#FFFFFF" }),
4993
+ isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Text, { style: { fontSize: 13, color: "#FFFFFF", fontWeight: theme.typography.fontWeight.semibold }, children: "Approve" }) : null
4858
4994
  ] })
4859
4995
  }
4860
4996
  )
4861
4997
  ] }),
4862
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
4998
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
4863
4999
  ReviewMergeRequestActionButton,
4864
5000
  {
4865
5001
  accessibilityLabel: "Test",
@@ -4867,9 +5003,9 @@ function ReviewMergeRequestCard({
4867
5003
  disabled: isBuilding || isTestingThis,
4868
5004
  onPress: onTest,
4869
5005
  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: [
4871
- /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(import_lucide_react_native8.Play, { size: 14, color: theme.colors.text }),
4872
- isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { style: { fontSize: 13, color: theme.colors.text, fontWeight: theme.typography.fontWeight.semibold }, children: "Test" }) : null
5006
+ children: isTestingThis ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_react_native40.ActivityIndicator, { size: "small", color: "#888" }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react_native40.View, { style: { flexDirection: "row", alignItems: "center", gap: isExpanded ? 4 : 0 }, children: [
5007
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react_native8.Play, { size: 14, color: theme.colors.text }),
5008
+ isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Text, { style: { fontSize: 13, color: theme.colors.text, fontWeight: theme.typography.fontWeight.semibold }, children: "Test" }) : null
4873
5009
  ] })
4874
5010
  }
4875
5011
  )
@@ -4880,7 +5016,7 @@ function ReviewMergeRequestCard({
4880
5016
  }
4881
5017
 
4882
5018
  // src/components/merge-requests/ReviewMergeRequestCarousel.tsx
4883
- var import_jsx_runtime41 = require("react/jsx-runtime");
5019
+ var import_jsx_runtime43 = require("react/jsx-runtime");
4884
5020
  function ReviewMergeRequestCarousel({
4885
5021
  mergeRequests,
4886
5022
  creatorStatsById,
@@ -4893,32 +5029,32 @@ function ReviewMergeRequestCarousel({
4893
5029
  style
4894
5030
  }) {
4895
5031
  const theme = useTheme();
4896
- const { width } = (0, import_react_native39.useWindowDimensions)();
4897
- const [expanded, setExpanded] = React28.useState({});
4898
- const carouselScrollX = React28.useRef(new import_react_native39.Animated.Value(0)).current;
5032
+ const { width } = (0, import_react_native41.useWindowDimensions)();
5033
+ const [expanded, setExpanded] = React30.useState({});
5034
+ const carouselScrollX = React30.useRef(new import_react_native41.Animated.Value(0)).current;
4899
5035
  const peekAmount = 24;
4900
5036
  const gap = 16;
4901
- const cardWidth = React28.useMemo(() => Math.max(1, width - theme.spacing.lg * 2 - peekAmount), [peekAmount, theme.spacing.lg, width]);
5037
+ const cardWidth = React30.useMemo(() => Math.max(1, width - theme.spacing.lg * 2 - peekAmount), [peekAmount, theme.spacing.lg, width]);
4902
5038
  const snapInterval = cardWidth + gap;
4903
5039
  const dotColor = theme.scheme === "dark" ? "#FFFFFF" : "#000000";
4904
5040
  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: [
4906
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
4907
- import_react_native39.FlatList,
5041
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_react_native41.View, { style: [{ marginHorizontal: -theme.spacing.lg }, style], children: [
5042
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
5043
+ import_react_native41.FlatList,
4908
5044
  {
4909
5045
  horizontal: true,
4910
5046
  data: mergeRequests,
4911
5047
  keyExtractor: (mr) => mr.id,
4912
5048
  showsHorizontalScrollIndicator: false,
4913
5049
  contentContainerStyle: { paddingHorizontal: theme.spacing.lg, paddingVertical: theme.spacing.sm },
4914
- ItemSeparatorComponent: () => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(import_react_native39.View, { style: { width: gap } }),
5050
+ ItemSeparatorComponent: () => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_react_native41.View, { style: { width: gap } }),
4915
5051
  snapToAlignment: "start",
4916
5052
  decelerationRate: "fast",
4917
5053
  snapToInterval: snapInterval,
4918
5054
  disableIntervalMomentum: true,
4919
5055
  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 } } }], {
5056
+ ListFooterComponent: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_react_native41.View, { style: { width: peekAmount } }),
5057
+ onScroll: import_react_native41.Animated.event([{ nativeEvent: { contentOffset: { x: carouselScrollX } } }], {
4922
5058
  useNativeDriver: false
4923
5059
  }),
4924
5060
  scrollEventThrottle: 16,
@@ -4929,7 +5065,7 @@ function ReviewMergeRequestCarousel({
4929
5065
  const isProcessing = Boolean(processingMrId && processingMrId === item.id);
4930
5066
  const isAnyProcessing = Boolean(processingMrId);
4931
5067
  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)(
5068
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_react_native41.View, { style: { width: cardWidth }, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
4933
5069
  ReviewMergeRequestCard,
4934
5070
  {
4935
5071
  mr: item,
@@ -4950,7 +5086,7 @@ function ReviewMergeRequestCarousel({
4950
5086
  }
4951
5087
  }
4952
5088
  ),
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) => {
5089
+ mergeRequests.length >= 1 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(import_react_native41.View, { style: { flexDirection: "row", justifyContent: "center", columnGap: 8, marginTop: theme.spacing.md }, children: mergeRequests.map((mr, index) => {
4954
5090
  const inputRange = [(index - 1) * snapInterval, index * snapInterval, (index + 1) * snapInterval];
4955
5091
  const scale = carouselScrollX.interpolate({
4956
5092
  inputRange,
@@ -4962,8 +5098,8 @@ function ReviewMergeRequestCarousel({
4962
5098
  outputRange: [0.4, 1, 0.4],
4963
5099
  extrapolate: "clamp"
4964
5100
  });
4965
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
4966
- import_react_native39.Animated.View,
5101
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
5102
+ import_react_native41.Animated.View,
4967
5103
  {
4968
5104
  style: {
4969
5105
  width: 8,
@@ -4981,7 +5117,7 @@ function ReviewMergeRequestCarousel({
4981
5117
  }
4982
5118
 
4983
5119
  // src/studio/ui/preview-panel/PreviewCollaborateSection.tsx
4984
- var import_jsx_runtime42 = require("react/jsx-runtime");
5120
+ var import_jsx_runtime44 = require("react/jsx-runtime");
4985
5121
  function PreviewCollaborateSection({
4986
5122
  canSubmitMergeRequest,
4987
5123
  incomingMergeRequests,
@@ -4997,13 +5133,13 @@ function PreviewCollaborateSection({
4997
5133
  onTestMr
4998
5134
  }) {
4999
5135
  const theme = useTheme();
5000
- const [submittingMr, setSubmittingMr] = React29.useState(false);
5136
+ const [submittingMr, setSubmittingMr] = React31.useState(false);
5001
5137
  const hasSection = canSubmitMergeRequest || incomingMergeRequests.length > 0 || outgoingMergeRequests.length > 0;
5002
5138
  if (!hasSection) return null;
5003
5139
  const showActionsSubtitle = canSubmitMergeRequest && onSubmitMergeRequest || onTestMr && incomingMergeRequests.length > 0;
5004
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
5005
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(SectionTitle, { marginTop: theme.spacing.xl, children: "Collaborate" }),
5006
- showActionsSubtitle ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5140
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
5141
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(SectionTitle, { marginTop: theme.spacing.xl, children: "Collaborate" }),
5142
+ showActionsSubtitle ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
5007
5143
  Text,
5008
5144
  {
5009
5145
  style: {
@@ -5018,13 +5154,13 @@ function PreviewCollaborateSection({
5018
5154
  children: "Actions"
5019
5155
  }
5020
5156
  ) : null,
5021
- canSubmitMergeRequest && onSubmitMergeRequest ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5157
+ canSubmitMergeRequest && onSubmitMergeRequest ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
5022
5158
  PressableCardRow,
5023
5159
  {
5024
5160
  accessibilityLabel: "Submit merge request",
5025
5161
  disabled: submittingMr,
5026
5162
  onPress: () => {
5027
- import_react_native40.Alert.alert(
5163
+ import_react_native42.Alert.alert(
5028
5164
  "Submit Merge Request",
5029
5165
  "Are you sure you want to submit your changes to the original app?",
5030
5166
  [
@@ -5049,8 +5185,8 @@ function PreviewCollaborateSection({
5049
5185
  borderColor: withAlpha("#03DAC6", 0.2),
5050
5186
  marginBottom: theme.spacing.sm
5051
5187
  },
5052
- left: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5053
- import_react_native40.View,
5188
+ left: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
5189
+ import_react_native42.View,
5054
5190
  {
5055
5191
  style: {
5056
5192
  width: 40,
@@ -5061,15 +5197,15 @@ function PreviewCollaborateSection({
5061
5197
  backgroundColor: withAlpha("#03DAC6", 0.1),
5062
5198
  marginRight: theme.spacing.lg
5063
5199
  },
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" })
5200
+ children: submittingMr ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native42.ActivityIndicator, { color: "#03DAC6", size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(MergeIcon, { width: 20, height: 20, color: "#03DAC6" })
5065
5201
  }
5066
5202
  ),
5067
- 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" }),
5068
- subtitle: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Text, { style: { color: theme.colors.textMuted, fontSize: 12, lineHeight: 16, marginTop: 2 }, children: "Ask to merge this remix to the original app" }),
5069
- right: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_lucide_react_native9.Send, { size: 16, color: "#03DAC6" })
5203
+ title: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Text, { style: { color: theme.colors.text, fontSize: 16, lineHeight: 20, fontWeight: theme.typography.fontWeight.semibold }, children: "Submit your new changes" }),
5204
+ subtitle: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Text, { style: { color: theme.colors.textMuted, fontSize: 12, lineHeight: 16, marginTop: 2 }, children: "Ask to merge this remix to the original app" }),
5205
+ right: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react_native9.Send, { size: 16, color: "#03DAC6" })
5070
5206
  }
5071
5207
  ) : null,
5072
- onTestMr && incomingMergeRequests.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5208
+ onTestMr && incomingMergeRequests.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
5073
5209
  ReviewMergeRequestCarousel,
5074
5210
  {
5075
5211
  mergeRequests: incomingMergeRequests,
@@ -5082,8 +5218,8 @@ function PreviewCollaborateSection({
5082
5218
  onTest: (mr) => onTestMr ? onTestMr(mr) : void 0
5083
5219
  }
5084
5220
  ) : null,
5085
- outgoingMergeRequests.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
5086
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
5221
+ outgoingMergeRequests.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
5222
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
5087
5223
  Text,
5088
5224
  {
5089
5225
  style: {
@@ -5099,13 +5235,13 @@ function PreviewCollaborateSection({
5099
5235
  children: "History"
5100
5236
  }
5101
5237
  ),
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))
5238
+ outgoingMergeRequests.map((mr) => /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native42.View, { style: { marginBottom: theme.spacing.sm }, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(MergeRequestStatusCard, { mergeRequest: toMergeRequestSummary(mr) }) }, mr.id))
5103
5239
  ] }) : null
5104
5240
  ] });
5105
5241
  }
5106
5242
 
5107
5243
  // src/studio/ui/preview-panel/usePreviewPanelData.ts
5108
- var React31 = __toESM(require("react"));
5244
+ var React33 = __toESM(require("react"));
5109
5245
 
5110
5246
  // src/data/apps/images/remote.ts
5111
5247
  var AppImagesRemoteDataSourceImpl = class extends BaseRemote {
@@ -5156,7 +5292,7 @@ var AppImagesRepositoryImpl = class extends BaseRepository {
5156
5292
  var appImagesRepository = new AppImagesRepositoryImpl(appImagesRemoteDataSource);
5157
5293
 
5158
5294
  // src/studio/hooks/useAppStats.ts
5159
- var React30 = __toESM(require("react"));
5295
+ var React32 = __toESM(require("react"));
5160
5296
  var Haptics2 = __toESM(require("expo-haptics"));
5161
5297
 
5162
5298
  // src/data/likes/remote.ts
@@ -5225,34 +5361,34 @@ function useAppStats({
5225
5361
  initialIsLiked = false,
5226
5362
  onOpenComments
5227
5363
  }) {
5228
- const [likeCount, setLikeCount] = React30.useState(initialLikes);
5229
- const [commentCount, setCommentCount] = React30.useState(initialComments);
5230
- const [forkCount, setForkCount] = React30.useState(initialForks);
5231
- const [isLiked, setIsLiked] = React30.useState(initialIsLiked);
5232
- const didMutateRef = React30.useRef(false);
5233
- const lastAppIdRef = React30.useRef("");
5234
- React30.useEffect(() => {
5364
+ const [likeCount, setLikeCount] = React32.useState(initialLikes);
5365
+ const [commentCount, setCommentCount] = React32.useState(initialComments);
5366
+ const [forkCount, setForkCount] = React32.useState(initialForks);
5367
+ const [isLiked, setIsLiked] = React32.useState(initialIsLiked);
5368
+ const didMutateRef = React32.useRef(false);
5369
+ const lastAppIdRef = React32.useRef("");
5370
+ React32.useEffect(() => {
5235
5371
  if (lastAppIdRef.current === appId) return;
5236
5372
  lastAppIdRef.current = appId;
5237
5373
  didMutateRef.current = false;
5238
5374
  }, [appId]);
5239
- React30.useEffect(() => {
5375
+ React32.useEffect(() => {
5240
5376
  if (didMutateRef.current) return;
5241
5377
  setLikeCount(initialLikes);
5242
5378
  }, [appId, initialLikes]);
5243
- React30.useEffect(() => {
5379
+ React32.useEffect(() => {
5244
5380
  if (didMutateRef.current) return;
5245
5381
  setCommentCount(initialComments);
5246
5382
  }, [appId, initialComments]);
5247
- React30.useEffect(() => {
5383
+ React32.useEffect(() => {
5248
5384
  if (didMutateRef.current) return;
5249
5385
  setForkCount(initialForks);
5250
5386
  }, [appId, initialForks]);
5251
- React30.useEffect(() => {
5387
+ React32.useEffect(() => {
5252
5388
  if (didMutateRef.current) return;
5253
5389
  setIsLiked(initialIsLiked);
5254
5390
  }, [appId, initialIsLiked]);
5255
- const handleLike = React30.useCallback(async () => {
5391
+ const handleLike = React32.useCallback(async () => {
5256
5392
  var _a, _b;
5257
5393
  if (!appId) return;
5258
5394
  didMutateRef.current = true;
@@ -5276,7 +5412,7 @@ function useAppStats({
5276
5412
  setLikeCount((prev) => Math.max(0, prev + (newIsLiked ? -1 : 1)));
5277
5413
  }
5278
5414
  }, [appId, isLiked, likeCount]);
5279
- const handleOpenComments = React30.useCallback(() => {
5415
+ const handleOpenComments = React32.useCallback(() => {
5280
5416
  if (!appId) return;
5281
5417
  try {
5282
5418
  void Haptics2.impactAsync(Haptics2.ImpactFeedbackStyle.Light);
@@ -5291,11 +5427,11 @@ function useAppStats({
5291
5427
  var LIKE_DEBUG_PREFIX = "[COMERGE_LIKE_DEBUG]";
5292
5428
  function usePreviewPanelData(params) {
5293
5429
  const { app, isOwner, outgoingMergeRequests, onOpenComments, commentCountOverride } = params;
5294
- const [imageUrl, setImageUrl] = React31.useState(null);
5295
- const [imageLoaded, setImageLoaded] = React31.useState(false);
5296
- const [insights, setInsights] = React31.useState({ likes: 0, comments: 0, forks: 0, downloads: 0 });
5297
- const [creator, setCreator] = React31.useState(null);
5298
- React31.useEffect(() => {
5430
+ const [imageUrl, setImageUrl] = React33.useState(null);
5431
+ const [imageLoaded, setImageLoaded] = React33.useState(false);
5432
+ const [insights, setInsights] = React33.useState({ likes: 0, comments: 0, forks: 0, downloads: 0 });
5433
+ const [creator, setCreator] = React33.useState(null);
5434
+ React33.useEffect(() => {
5299
5435
  if (!(app == null ? void 0 : app.id)) return;
5300
5436
  let cancelled = false;
5301
5437
  (async () => {
@@ -5310,7 +5446,7 @@ function usePreviewPanelData(params) {
5310
5446
  cancelled = true;
5311
5447
  };
5312
5448
  }, [app == null ? void 0 : app.id]);
5313
- React31.useEffect(() => {
5449
+ React33.useEffect(() => {
5314
5450
  if (!(app == null ? void 0 : app.createdBy)) return;
5315
5451
  let cancelled = false;
5316
5452
  (async () => {
@@ -5326,10 +5462,10 @@ function usePreviewPanelData(params) {
5326
5462
  cancelled = true;
5327
5463
  };
5328
5464
  }, [app == null ? void 0 : app.createdBy]);
5329
- React31.useEffect(() => {
5465
+ React33.useEffect(() => {
5330
5466
  setImageLoaded(false);
5331
5467
  }, [app == null ? void 0 : app.id]);
5332
- React31.useEffect(() => {
5468
+ React33.useEffect(() => {
5333
5469
  if (!(app == null ? void 0 : app.id)) return;
5334
5470
  let cancelled = false;
5335
5471
  (async () => {
@@ -5354,7 +5490,7 @@ function usePreviewPanelData(params) {
5354
5490
  cancelled = true;
5355
5491
  };
5356
5492
  }, [app == null ? void 0 : app.id]);
5357
- React31.useEffect(() => {
5493
+ React33.useEffect(() => {
5358
5494
  if (!(app == null ? void 0 : app.id)) return;
5359
5495
  log.debug(
5360
5496
  `${LIKE_DEBUG_PREFIX} usePreviewPanelData.appChanged appId=${app.id} app.isLiked=${String(app.isLiked)}`
@@ -5368,7 +5504,7 @@ function usePreviewPanelData(params) {
5368
5504
  initialIsLiked: Boolean(app == null ? void 0 : app.isLiked),
5369
5505
  onOpenComments
5370
5506
  });
5371
- const canSubmitMergeRequest = React31.useMemo(() => {
5507
+ const canSubmitMergeRequest = React33.useMemo(() => {
5372
5508
  if (!isOwner) return false;
5373
5509
  if (!app) return false;
5374
5510
  if (!app.forkedFromAppId) return false;
@@ -5390,7 +5526,7 @@ function usePreviewPanelData(params) {
5390
5526
  }
5391
5527
 
5392
5528
  // src/studio/ui/PreviewPanel.tsx
5393
- var import_jsx_runtime43 = require("react/jsx-runtime");
5529
+ var import_jsx_runtime45 = require("react/jsx-runtime");
5394
5530
  function PreviewPanel({
5395
5531
  app,
5396
5532
  loading,
@@ -5421,16 +5557,16 @@ function PreviewPanel({
5421
5557
  onOpenComments,
5422
5558
  commentCountOverride
5423
5559
  });
5424
- const header = /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(PreviewPanelHeader, { isOwner, onClose, onNavigateHome, onGoToChat });
5560
+ const header = /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(PreviewPanelHeader, { isOwner, onClose, onNavigateHome, onGoToChat });
5425
5561
  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 } }),
5429
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { variant: "bodyMuted", children: "Loading app\u2026" })
5562
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(PreviewPage, { header, children: /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(import_react_native43.View, { style: { flex: 1, justifyContent: "center", alignItems: "center", padding: 24 }, children: [
5563
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_react_native43.ActivityIndicator, {}),
5564
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(import_react_native43.View, { style: { height: 12 } }),
5565
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Text, { variant: "bodyMuted", children: "Loading app\u2026" })
5430
5566
  ] }) });
5431
5567
  }
5432
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(PreviewPage, { header, children: [
5433
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
5568
+ return /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(PreviewPage, { header, children: [
5569
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
5434
5570
  PreviewHeroSection,
5435
5571
  {
5436
5572
  appStatus: app.status,
@@ -5448,8 +5584,8 @@ function PreviewPanel({
5448
5584
  }
5449
5585
  }
5450
5586
  ),
5451
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(PreviewMetaSection, { app, isOwner, creator, downloadsCount: insights.downloads }),
5452
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
5587
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(PreviewMetaSection, { app, isOwner, creator, downloadsCount: insights.downloads }),
5588
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
5453
5589
  PreviewCustomizeSection,
5454
5590
  {
5455
5591
  app,
@@ -5460,7 +5596,7 @@ function PreviewPanel({
5460
5596
  onStartDraw
5461
5597
  }
5462
5598
  ),
5463
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
5599
+ /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
5464
5600
  PreviewCollaborateSection,
5465
5601
  {
5466
5602
  canSubmitMergeRequest,
@@ -5481,23 +5617,23 @@ function PreviewPanel({
5481
5617
  }
5482
5618
 
5483
5619
  // src/studio/ui/ChatPanel.tsx
5484
- var React36 = __toESM(require("react"));
5485
- var import_react_native49 = require("react-native");
5620
+ var React38 = __toESM(require("react"));
5621
+ var import_react_native51 = require("react-native");
5486
5622
 
5487
5623
  // src/components/chat/ChatPage.tsx
5488
- var React34 = __toESM(require("react"));
5489
- var import_react_native45 = require("react-native");
5624
+ var React36 = __toESM(require("react"));
5625
+ var import_react_native47 = require("react-native");
5490
5626
  var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
5491
5627
 
5492
5628
  // src/components/chat/ChatMessageList.tsx
5493
- var React33 = __toESM(require("react"));
5494
- var import_react_native44 = require("react-native");
5629
+ var React35 = __toESM(require("react"));
5630
+ var import_react_native46 = require("react-native");
5495
5631
  var import_bottom_sheet5 = require("@gorhom/bottom-sheet");
5496
5632
 
5497
5633
  // src/components/chat/ChatMessageBubble.tsx
5498
- var import_react_native42 = require("react-native");
5634
+ var import_react_native44 = require("react-native");
5499
5635
  var import_lucide_react_native10 = require("lucide-react-native");
5500
- var import_jsx_runtime44 = require("react/jsx-runtime");
5636
+ var import_jsx_runtime46 = require("react/jsx-runtime");
5501
5637
  function ChatMessageBubble({ message, renderContent, style }) {
5502
5638
  var _a, _b;
5503
5639
  const theme = useTheme();
@@ -5511,7 +5647,7 @@ function ChatMessageBubble({ message, renderContent, style }) {
5511
5647
  const bubbleVariant = isHuman ? "surface" : "surfaceRaised";
5512
5648
  const cornerStyle = isHuman ? { borderTopRightRadius: 0 } : { borderTopLeftRadius: 0 };
5513
5649
  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)(
5650
+ return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react_native44.View, { style: [align, style], children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
5515
5651
  Surface,
5516
5652
  {
5517
5653
  variant: bubbleVariant,
@@ -5526,34 +5662,34 @@ function ChatMessageBubble({ message, renderContent, style }) {
5526
5662
  },
5527
5663
  cornerStyle
5528
5664
  ],
5529
- children: /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_react_native42.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
5530
- 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
- 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 }) })
5665
+ children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_react_native44.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
5666
+ isMergeCompleted ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react_native10.CheckCheck, { size: 16, color: theme.colors.success, style: { marginRight: theme.spacing.sm } }) : null,
5667
+ isMergeApproved ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react_native10.GitMerge, { size: 16, color: theme.colors.text, style: { marginRight: theme.spacing.sm } }) : null,
5668
+ /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react_native44.View, { style: { flexShrink: 1, minWidth: 0 }, children: renderContent ? renderContent(message) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(MarkdownText, { markdown: message.content, variant: "chat", bodyColor }) })
5533
5669
  ] })
5534
5670
  }
5535
5671
  ) });
5536
5672
  }
5537
5673
 
5538
5674
  // src/components/chat/TypingIndicator.tsx
5539
- var React32 = __toESM(require("react"));
5540
- var import_react_native43 = require("react-native");
5541
- var import_jsx_runtime45 = require("react/jsx-runtime");
5675
+ var React34 = __toESM(require("react"));
5676
+ var import_react_native45 = require("react-native");
5677
+ var import_jsx_runtime47 = require("react/jsx-runtime");
5542
5678
  function TypingIndicator({ style }) {
5543
5679
  const theme = useTheme();
5544
5680
  const dotColor = theme.colors.textSubtle;
5545
- 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)],
5681
+ const anims = React34.useMemo(
5682
+ () => [new import_react_native45.Animated.Value(0.3), new import_react_native45.Animated.Value(0.3), new import_react_native45.Animated.Value(0.3)],
5547
5683
  []
5548
5684
  );
5549
- React32.useEffect(() => {
5685
+ React34.useEffect(() => {
5550
5686
  const loops = [];
5551
5687
  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 })
5688
+ const seq = import_react_native45.Animated.sequence([
5689
+ import_react_native45.Animated.timing(a, { toValue: 1, duration: 420, useNativeDriver: true, delay: idx * 140 }),
5690
+ import_react_native45.Animated.timing(a, { toValue: 0.3, duration: 420, useNativeDriver: true })
5555
5691
  ]);
5556
- const loop = import_react_native43.Animated.loop(seq);
5692
+ const loop = import_react_native45.Animated.loop(seq);
5557
5693
  loops.push(loop);
5558
5694
  loop.start();
5559
5695
  });
@@ -5561,8 +5697,8 @@ function TypingIndicator({ style }) {
5561
5697
  loops.forEach((l) => l.stop());
5562
5698
  };
5563
5699
  }, [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,
5700
+ return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(import_react_native45.View, { style: [{ flexDirection: "row", alignItems: "center" }, style], children: anims.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
5701
+ import_react_native45.Animated.View,
5566
5702
  {
5567
5703
  style: {
5568
5704
  width: 8,
@@ -5571,7 +5707,7 @@ function TypingIndicator({ style }) {
5571
5707
  marginHorizontal: 3,
5572
5708
  backgroundColor: dotColor,
5573
5709
  opacity: a,
5574
- transform: [{ translateY: import_react_native43.Animated.multiply(import_react_native43.Animated.subtract(a, 0.3), 2) }]
5710
+ transform: [{ translateY: import_react_native45.Animated.multiply(import_react_native45.Animated.subtract(a, 0.3), 2) }]
5575
5711
  }
5576
5712
  },
5577
5713
  i
@@ -5579,8 +5715,8 @@ function TypingIndicator({ style }) {
5579
5715
  }
5580
5716
 
5581
5717
  // src/components/chat/ChatMessageList.tsx
5582
- var import_jsx_runtime46 = require("react/jsx-runtime");
5583
- var ChatMessageList = React33.forwardRef(
5718
+ var import_jsx_runtime48 = require("react/jsx-runtime");
5719
+ var ChatMessageList = React35.forwardRef(
5584
5720
  ({
5585
5721
  messages,
5586
5722
  showTypingIndicator = false,
@@ -5591,23 +5727,23 @@ var ChatMessageList = React33.forwardRef(
5591
5727
  nearBottomThreshold = 200
5592
5728
  }, ref) => {
5593
5729
  const theme = useTheme();
5594
- const listRef = React33.useRef(null);
5595
- const nearBottomRef = React33.useRef(true);
5596
- const initialScrollDoneRef = React33.useRef(false);
5597
- const lastMessageIdRef = React33.useRef(null);
5598
- const scrollToBottom = React33.useCallback((options) => {
5730
+ const listRef = React35.useRef(null);
5731
+ const nearBottomRef = React35.useRef(true);
5732
+ const initialScrollDoneRef = React35.useRef(false);
5733
+ const lastMessageIdRef = React35.useRef(null);
5734
+ const data = React35.useMemo(() => {
5735
+ return [...messages].reverse();
5736
+ }, [messages]);
5737
+ const scrollToBottom = React35.useCallback((options) => {
5599
5738
  var _a;
5600
5739
  const animated = (options == null ? void 0 : options.animated) ?? true;
5601
- (_a = listRef.current) == null ? void 0 : _a.scrollToEnd({ animated });
5740
+ (_a = listRef.current) == null ? void 0 : _a.scrollToOffset({ offset: 0, animated });
5602
5741
  }, []);
5603
- React33.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
5604
- const handleScroll = React33.useCallback(
5742
+ React35.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
5743
+ const handleScroll = React35.useCallback(
5605
5744
  (e) => {
5606
5745
  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
- );
5746
+ const distanceFromBottom = Math.max(contentOffset.y - Math.max(bottomInset, 0), 0);
5611
5747
  const isNear = distanceFromBottom <= nearBottomThreshold;
5612
5748
  if (nearBottomRef.current !== isNear) {
5613
5749
  nearBottomRef.current = isNear;
@@ -5616,16 +5752,7 @@ var ChatMessageList = React33.forwardRef(
5616
5752
  },
5617
5753
  [bottomInset, nearBottomThreshold, onNearBottomChange]
5618
5754
  );
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
- React33.useEffect(() => {
5755
+ React35.useEffect(() => {
5629
5756
  if (!initialScrollDoneRef.current) return;
5630
5757
  const lastId = messages.length > 0 ? messages[messages.length - 1].id : null;
5631
5758
  const prevLastId = lastMessageIdRef.current;
@@ -5635,42 +5762,44 @@ var ChatMessageList = React33.forwardRef(
5635
5762
  const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
5636
5763
  return () => cancelAnimationFrame(id);
5637
5764
  }, [messages, scrollToBottom]);
5638
- React33.useEffect(() => {
5765
+ React35.useEffect(() => {
5639
5766
  if (showTypingIndicator && nearBottomRef.current) {
5640
5767
  const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
5641
5768
  return () => cancelAnimationFrame(id);
5642
5769
  }
5643
5770
  return void 0;
5644
5771
  }, [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
- return /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
5772
+ return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
5652
5773
  import_bottom_sheet5.BottomSheetFlatList,
5653
5774
  {
5654
5775
  ref: listRef,
5655
- data: messages,
5776
+ inverted: true,
5777
+ data,
5656
5778
  keyExtractor: (m) => m.id,
5657
- keyboardDismissMode: import_react_native44.Platform.OS === "ios" ? "interactive" : "on-drag",
5658
5779
  keyboardShouldPersistTaps: "handled",
5659
5780
  onScroll: handleScroll,
5660
5781
  scrollEventThrottle: 16,
5661
5782
  showsVerticalScrollIndicator: false,
5783
+ onContentSizeChange: () => {
5784
+ if (initialScrollDoneRef.current) return;
5785
+ initialScrollDoneRef.current = true;
5786
+ lastMessageIdRef.current = messages.length > 0 ? messages[messages.length - 1].id : null;
5787
+ nearBottomRef.current = true;
5788
+ onNearBottomChange == null ? void 0 : onNearBottomChange(true);
5789
+ requestAnimationFrame(() => scrollToBottom({ animated: false }));
5790
+ },
5662
5791
  contentContainerStyle: [
5663
5792
  {
5664
5793
  paddingHorizontal: theme.spacing.lg,
5665
- paddingTop: theme.spacing.sm,
5666
- paddingBottom: theme.spacing.sm
5794
+ paddingVertical: theme.spacing.sm
5667
5795
  },
5668
5796
  contentStyle
5669
5797
  ],
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
5798
+ ItemSeparatorComponent: () => /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_react_native46.View, { style: { height: theme.spacing.sm } }),
5799
+ renderItem: ({ item }) => /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(ChatMessageBubble, { message: item, renderContent: renderMessageContent }),
5800
+ ListHeaderComponent: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(import_react_native46.View, { children: [
5801
+ showTypingIndicator ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_react_native46.View, { style: { marginTop: theme.spacing.sm, alignSelf: "flex-start", paddingHorizontal: theme.spacing.lg }, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(TypingIndicator, {}) }) : null,
5802
+ bottomInset > 0 ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(import_react_native46.View, { style: { height: bottomInset } }) : null
5674
5803
  ] })
5675
5804
  }
5676
5805
  );
@@ -5679,7 +5808,7 @@ var ChatMessageList = React33.forwardRef(
5679
5808
  ChatMessageList.displayName = "ChatMessageList";
5680
5809
 
5681
5810
  // src/components/chat/ChatPage.tsx
5682
- var import_jsx_runtime47 = require("react/jsx-runtime");
5811
+ var import_jsx_runtime49 = require("react/jsx-runtime");
5683
5812
  function ChatPage({
5684
5813
  header,
5685
5814
  messages,
@@ -5689,44 +5818,45 @@ function ChatPage({
5689
5818
  composer,
5690
5819
  overlay,
5691
5820
  style,
5821
+ composerHorizontalPadding,
5692
5822
  onNearBottomChange,
5693
5823
  listRef
5694
5824
  }) {
5695
5825
  const theme = useTheme();
5696
5826
  const insets = (0, import_react_native_safe_area_context4.useSafeAreaInsets)();
5697
- const [composerHeight, setComposerHeight] = React34.useState(0);
5698
- const [keyboardVisible, setKeyboardVisible] = React34.useState(false);
5699
- 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));
5827
+ const [composerHeight, setComposerHeight] = React36.useState(0);
5828
+ const [keyboardVisible, setKeyboardVisible] = React36.useState(false);
5829
+ React36.useEffect(() => {
5830
+ if (import_react_native47.Platform.OS !== "ios") return;
5831
+ const show = import_react_native47.Keyboard.addListener("keyboardWillShow", () => setKeyboardVisible(true));
5832
+ const hide = import_react_native47.Keyboard.addListener("keyboardWillHide", () => setKeyboardVisible(false));
5703
5833
  return () => {
5704
5834
  show.remove();
5705
5835
  hide.remove();
5706
5836
  };
5707
5837
  }, []);
5708
- const footerBottomPadding = import_react_native45.Platform.OS === "ios" ? keyboardVisible ? 0 : insets.bottom : insets.bottom + 10;
5838
+ const footerBottomPadding = import_react_native47.Platform.OS === "ios" ? keyboardVisible ? 0 : insets.bottom : insets.bottom + 10;
5709
5839
  const overlayBottom = composerHeight + footerBottomPadding + theme.spacing.lg;
5710
5840
  const bottomInset = composerHeight + footerBottomPadding + theme.spacing.xl;
5711
- const resolvedOverlay = React34.useMemo(() => {
5841
+ const resolvedOverlay = React36.useMemo(() => {
5712
5842
  var _a;
5713
5843
  if (!overlay) return null;
5714
- if (!React34.isValidElement(overlay)) return overlay;
5844
+ if (!React36.isValidElement(overlay)) return overlay;
5715
5845
  const prevStyle = (_a = overlay.props) == null ? void 0 : _a.style;
5716
- return React34.cloneElement(overlay, {
5846
+ return React36.cloneElement(overlay, {
5717
5847
  style: [prevStyle, { bottom: overlayBottom }]
5718
5848
  });
5719
5849
  }, [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: [
5724
- /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(
5725
- import_react_native45.View,
5850
+ return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_react_native47.View, { style: [{ flex: 1 }, style], children: [
5851
+ header ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_react_native47.View, { children: header }) : null,
5852
+ topBanner ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_react_native47.View, { style: { paddingHorizontal: theme.spacing.lg, paddingTop: theme.spacing.sm }, children: topBanner }) : null,
5853
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_react_native47.View, { style: { flex: 1 }, children: [
5854
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
5855
+ import_react_native47.View,
5726
5856
  {
5727
5857
  style: { flex: 1 },
5728
5858
  children: [
5729
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
5859
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
5730
5860
  ChatMessageList,
5731
5861
  {
5732
5862
  ref: listRef,
@@ -5741,19 +5871,19 @@ function ChatPage({
5741
5871
  ]
5742
5872
  }
5743
5873
  ),
5744
- /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
5745
- import_react_native45.View,
5874
+ /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
5875
+ import_react_native47.View,
5746
5876
  {
5747
5877
  style: {
5748
5878
  position: "absolute",
5749
5879
  left: 0,
5750
5880
  right: 0,
5751
5881
  bottom: 0,
5752
- paddingHorizontal: theme.spacing.lg,
5882
+ paddingHorizontal: composerHorizontalPadding ?? theme.spacing.md,
5753
5883
  paddingTop: theme.spacing.sm,
5754
5884
  paddingBottom: footerBottomPadding
5755
5885
  },
5756
- children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
5886
+ children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
5757
5887
  ChatComposer,
5758
5888
  {
5759
5889
  ...composer,
@@ -5768,15 +5898,15 @@ function ChatPage({
5768
5898
  }
5769
5899
 
5770
5900
  // src/components/chat/ScrollToBottomButton.tsx
5771
- var React35 = __toESM(require("react"));
5772
- var import_react_native46 = require("react-native");
5901
+ var React37 = __toESM(require("react"));
5902
+ var import_react_native48 = require("react-native");
5773
5903
  var import_react_native_reanimated2 = __toESM(require("react-native-reanimated"));
5774
- var import_jsx_runtime48 = require("react/jsx-runtime");
5904
+ var import_jsx_runtime50 = require("react/jsx-runtime");
5775
5905
  function ScrollToBottomButton({ visible, onPress, children, style }) {
5776
5906
  const theme = useTheme();
5777
5907
  const progress = (0, import_react_native_reanimated2.useSharedValue)(visible ? 1 : 0);
5778
- const [pressed, setPressed] = React35.useState(false);
5779
- React35.useEffect(() => {
5908
+ const [pressed, setPressed] = React37.useState(false);
5909
+ React37.useEffect(() => {
5780
5910
  progress.value = (0, import_react_native_reanimated2.withTiming)(visible ? 1 : 0, { duration: 200, easing: import_react_native_reanimated2.Easing.out(import_react_native_reanimated2.Easing.ease) });
5781
5911
  }, [progress, visible]);
5782
5912
  const animStyle = (0, import_react_native_reanimated2.useAnimatedStyle)(() => ({
@@ -5785,7 +5915,7 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
5785
5915
  }));
5786
5916
  const bg = theme.scheme === "dark" ? "rgba(39,39,42,0.9)" : "rgba(244,244,245,0.95)";
5787
5917
  const border = theme.scheme === "dark" ? withAlpha("#FFFFFF", 0.12) : withAlpha("#000000", 0.08);
5788
- return /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
5918
+ return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
5789
5919
  import_react_native_reanimated2.default.View,
5790
5920
  {
5791
5921
  pointerEvents: visible ? "auto" : "none",
@@ -5799,8 +5929,8 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
5799
5929
  style,
5800
5930
  animStyle
5801
5931
  ],
5802
- children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
5803
- import_react_native46.View,
5932
+ children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
5933
+ import_react_native48.View,
5804
5934
  {
5805
5935
  style: {
5806
5936
  width: 44,
@@ -5818,8 +5948,8 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
5818
5948
  elevation: 5,
5819
5949
  opacity: pressed ? 0.85 : 1
5820
5950
  },
5821
- children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(
5822
- import_react_native46.Pressable,
5951
+ children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
5952
+ import_react_native48.Pressable,
5823
5953
  {
5824
5954
  onPress,
5825
5955
  onPressIn: () => setPressed(true),
@@ -5836,16 +5966,16 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
5836
5966
  }
5837
5967
 
5838
5968
  // src/components/chat/ChatHeader.tsx
5839
- var import_react_native47 = require("react-native");
5840
- var import_jsx_runtime49 = require("react/jsx-runtime");
5969
+ var import_react_native49 = require("react-native");
5970
+ var import_jsx_runtime51 = require("react/jsx-runtime");
5841
5971
  function ChatHeader({ left, right, center, style }) {
5842
- const flattenedStyle = import_react_native47.StyleSheet.flatten([
5972
+ const flattenedStyle = import_react_native49.StyleSheet.flatten([
5843
5973
  {
5844
5974
  paddingTop: 0
5845
5975
  },
5846
5976
  style
5847
5977
  ]);
5848
- return /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
5978
+ return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
5849
5979
  StudioSheetHeader,
5850
5980
  {
5851
5981
  left,
@@ -5857,13 +5987,13 @@ function ChatHeader({ left, right, center, style }) {
5857
5987
  }
5858
5988
 
5859
5989
  // src/components/chat/ForkNoticeBanner.tsx
5860
- var import_react_native48 = require("react-native");
5861
- var import_jsx_runtime50 = require("react/jsx-runtime");
5990
+ var import_react_native50 = require("react-native");
5991
+ var import_jsx_runtime52 = require("react/jsx-runtime");
5862
5992
  function ForkNoticeBanner({ isOwner = true, title, description, style }) {
5863
5993
  const theme = useTheme();
5864
5994
  const resolvedTitle = title ?? (isOwner ? "Remixed app" : "Remix app");
5865
5995
  const resolvedDescription = description ?? (isOwner ? "Any changes you make will be a remix of the original app. You can view the edited version in the Remix tab in your apps page." : "Once you make edits, this remixed version will appear on your Remixed apps page.");
5866
- return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
5996
+ return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
5867
5997
  Card,
5868
5998
  {
5869
5999
  variant: "surfaceRaised",
@@ -5878,8 +6008,8 @@ function ForkNoticeBanner({ isOwner = true, title, description, style }) {
5878
6008
  },
5879
6009
  style
5880
6010
  ],
5881
- children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_react_native48.View, { style: { minWidth: 0 }, children: [
5882
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
6011
+ children: /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_react_native50.View, { style: { minWidth: 0 }, children: [
6012
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
5883
6013
  Text,
5884
6014
  {
5885
6015
  style: {
@@ -5893,7 +6023,7 @@ function ForkNoticeBanner({ isOwner = true, title, description, style }) {
5893
6023
  children: resolvedTitle
5894
6024
  }
5895
6025
  ),
5896
- /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
6026
+ /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
5897
6027
  Text,
5898
6028
  {
5899
6029
  style: {
@@ -5911,7 +6041,7 @@ function ForkNoticeBanner({ isOwner = true, title, description, style }) {
5911
6041
  }
5912
6042
 
5913
6043
  // src/studio/ui/ChatPanel.tsx
5914
- var import_jsx_runtime51 = require("react/jsx-runtime");
6044
+ var import_jsx_runtime53 = require("react/jsx-runtime");
5915
6045
  function ChatPanel({
5916
6046
  title = "Chat",
5917
6047
  autoFocusComposer = false,
@@ -5931,9 +6061,9 @@ function ChatPanel({
5931
6061
  onStartDraw,
5932
6062
  onSend
5933
6063
  }) {
5934
- const listRef = React36.useRef(null);
5935
- const [nearBottom, setNearBottom] = React36.useState(true);
5936
- const handleSend = React36.useCallback(
6064
+ const listRef = React38.useRef(null);
6065
+ const [nearBottom, setNearBottom] = React38.useState(true);
6066
+ const handleSend = React38.useCallback(
5937
6067
  async (text, composerAttachments) => {
5938
6068
  const all = composerAttachments ?? attachments;
5939
6069
  await onSend(text, all.length > 0 ? all : void 0);
@@ -5947,25 +6077,25 @@ function ChatPanel({
5947
6077
  },
5948
6078
  [attachments, nearBottom, onClearAttachments, onSend]
5949
6079
  );
5950
- const handleScrollToBottom = React36.useCallback(() => {
6080
+ const handleScrollToBottom = React38.useCallback(() => {
5951
6081
  var _a;
5952
6082
  (_a = listRef.current) == null ? void 0 : _a.scrollToBottom({ animated: true });
5953
6083
  }, []);
5954
- const header = /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
6084
+ const header = /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
5955
6085
  ChatHeader,
5956
6086
  {
5957
- left: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_react_native49.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
5958
- /* @__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
- 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
6087
+ left: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_react_native51.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
6088
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(StudioSheetHeaderIconButton, { onPress: onBack, accessibilityLabel: "Back", style: { marginRight: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(IconBack, { size: 20, colorToken: "floatingContent" }) }),
6089
+ onNavigateHome ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(StudioSheetHeaderIconButton, { onPress: onNavigateHome, accessibilityLabel: "Home", children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(IconHome, { size: 20, colorToken: "floatingContent" }) }) : null
5960
6090
  ] }),
5961
- right: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(import_react_native49.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
5962
- 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
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(IconClose, { size: 20, colorToken: "floatingContent" }) })
6091
+ right: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_react_native51.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
6092
+ onStartDraw ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(StudioSheetHeaderIconButton, { onPress: onStartDraw, accessibilityLabel: "Draw", intent: "danger", style: { marginRight: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(IconDraw, { size: 20, colorToken: "onDanger" }) }) : null,
6093
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(IconClose, { size: 20, colorToken: "floatingContent" }) })
5964
6094
  ] }),
5965
6095
  center: null
5966
6096
  }
5967
6097
  );
5968
- const topBanner = shouldForkOnEdit ? /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
6098
+ const topBanner = shouldForkOnEdit ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
5969
6099
  ForkNoticeBanner,
5970
6100
  {
5971
6101
  isOwner: !shouldForkOnEdit,
@@ -5974,32 +6104,33 @@ function ChatPanel({
5974
6104
  ) : null;
5975
6105
  const showMessagesLoading = Boolean(loading) && messages.length === 0 || forking;
5976
6106
  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 } }),
5983
- /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(Text, { variant: "bodyMuted", children: forking ? "Creating your copy\u2026" : "Loading messages\u2026" })
6107
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_react_native51.View, { style: { flex: 1 }, children: [
6108
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native51.View, { children: header }),
6109
+ topBanner ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native51.View, { style: { paddingHorizontal: 16, paddingTop: 8 }, children: topBanner }) : null,
6110
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_react_native51.View, { style: { flex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 24, paddingVertical: 12 }, children: [
6111
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native51.ActivityIndicator, {}),
6112
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native51.View, { style: { height: 12 } }),
6113
+ /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Text, { variant: "bodyMuted", children: forking ? "Creating your copy\u2026" : "Loading messages\u2026" })
5984
6114
  ] })
5985
6115
  ] });
5986
6116
  }
5987
- return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
6117
+ return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
5988
6118
  ChatPage,
5989
6119
  {
5990
6120
  header,
5991
6121
  messages,
5992
6122
  showTypingIndicator,
5993
6123
  topBanner,
6124
+ composerHorizontalPadding: 0,
5994
6125
  listRef,
5995
6126
  onNearBottomChange: setNearBottom,
5996
- overlay: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
6127
+ overlay: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
5997
6128
  ScrollToBottomButton,
5998
6129
  {
5999
6130
  visible: !nearBottom,
6000
6131
  onPress: handleScrollToBottom,
6001
6132
  style: { bottom: 80 },
6002
- children: /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(IconArrowDown, { size: 20, colorToken: "floatingContent" })
6133
+ children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(IconArrowDown, { size: 20, colorToken: "floatingContent" })
6003
6134
  }
6004
6135
  ),
6005
6136
  composer: {
@@ -6020,12 +6151,12 @@ function ChatPanel({
6020
6151
  }
6021
6152
 
6022
6153
  // src/components/dialogs/ConfirmMergeRequestDialog.tsx
6023
- var React37 = __toESM(require("react"));
6024
- var import_react_native51 = require("react-native");
6154
+ var React39 = __toESM(require("react"));
6155
+ var import_react_native53 = require("react-native");
6025
6156
 
6026
6157
  // src/components/primitives/Modal.tsx
6027
- var import_react_native50 = require("react-native");
6028
- var import_jsx_runtime52 = require("react/jsx-runtime");
6158
+ var import_react_native52 = require("react-native");
6159
+ var import_jsx_runtime54 = require("react/jsx-runtime");
6029
6160
  function Modal({
6030
6161
  visible,
6031
6162
  onRequestClose,
@@ -6034,30 +6165,30 @@ function Modal({
6034
6165
  contentStyle
6035
6166
  }) {
6036
6167
  const theme = useTheme();
6037
- return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
6038
- import_react_native50.Modal,
6168
+ return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
6169
+ import_react_native52.Modal,
6039
6170
  {
6040
6171
  visible,
6041
6172
  transparent: true,
6042
6173
  animationType: "fade",
6043
6174
  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: [
6045
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
6046
- import_react_native50.Pressable,
6175
+ children: /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(import_react_native52.View, { style: { flex: 1, backgroundColor: theme.colors.backdrop, justifyContent: "center", padding: theme.spacing.lg }, children: [
6176
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
6177
+ import_react_native52.Pressable,
6047
6178
  {
6048
6179
  accessibilityRole: "button",
6049
6180
  onPress: dismissOnBackdropPress ? onRequestClose : void 0,
6050
6181
  style: { position: "absolute", inset: 0 }
6051
6182
  }
6052
6183
  ),
6053
- /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(Card, { variant: "surfaceRaised", padded: true, style: [{ borderRadius: theme.radii.xl }, contentStyle], children })
6184
+ /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(Card, { variant: "surfaceRaised", padded: true, style: [{ borderRadius: theme.radii.xl }, contentStyle], children })
6054
6185
  ] })
6055
6186
  }
6056
6187
  );
6057
6188
  }
6058
6189
 
6059
6190
  // src/components/dialogs/ConfirmMergeRequestDialog.tsx
6060
- var import_jsx_runtime53 = require("react/jsx-runtime");
6191
+ var import_jsx_runtime55 = require("react/jsx-runtime");
6061
6192
  function ConfirmMergeRequestDialog({
6062
6193
  visible,
6063
6194
  onOpenChange,
@@ -6068,14 +6199,14 @@ function ConfirmMergeRequestDialog({
6068
6199
  onTestFirst
6069
6200
  }) {
6070
6201
  const theme = useTheme();
6071
- const close = React37.useCallback(() => onOpenChange(false), [onOpenChange]);
6202
+ const close = React39.useCallback(() => onOpenChange(false), [onOpenChange]);
6072
6203
  const canConfirm = Boolean(mergeRequest) && !approveDisabled;
6073
- const handleConfirm = React37.useCallback(() => {
6204
+ const handleConfirm = React39.useCallback(() => {
6074
6205
  if (!mergeRequest) return;
6075
6206
  onOpenChange(false);
6076
6207
  void onConfirm();
6077
6208
  }, [mergeRequest, onConfirm, onOpenChange]);
6078
- const handleTestFirst = React37.useCallback(() => {
6209
+ const handleTestFirst = React39.useCallback(() => {
6079
6210
  if (!mergeRequest) return;
6080
6211
  onOpenChange(false);
6081
6212
  void onTestFirst(mergeRequest);
@@ -6087,7 +6218,7 @@ function ConfirmMergeRequestDialog({
6087
6218
  justifyContent: "center",
6088
6219
  alignSelf: "stretch"
6089
6220
  };
6090
- return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
6221
+ return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(
6091
6222
  Modal,
6092
6223
  {
6093
6224
  visible,
@@ -6098,7 +6229,7 @@ function ConfirmMergeRequestDialog({
6098
6229
  backgroundColor: theme.colors.background
6099
6230
  },
6100
6231
  children: [
6101
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native51.View, { children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6232
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_react_native53.View, { children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6102
6233
  Text,
6103
6234
  {
6104
6235
  style: {
@@ -6110,9 +6241,9 @@ function ConfirmMergeRequestDialog({
6110
6241
  children: "Are you sure you want to approve this merge request?"
6111
6242
  }
6112
6243
  ) }),
6113
- /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_react_native51.View, { style: { marginTop: 16 }, children: [
6114
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6115
- import_react_native51.View,
6244
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_react_native53.View, { style: { marginTop: 16 }, children: [
6245
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6246
+ import_react_native53.View,
6116
6247
  {
6117
6248
  style: [
6118
6249
  fullWidthButtonBase,
@@ -6121,22 +6252,22 @@ function ConfirmMergeRequestDialog({
6121
6252
  opacity: canConfirm ? 1 : 0.5
6122
6253
  }
6123
6254
  ],
6124
- children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6125
- import_react_native51.Pressable,
6255
+ children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6256
+ import_react_native53.Pressable,
6126
6257
  {
6127
6258
  accessibilityRole: "button",
6128
6259
  accessibilityLabel: "Approve Merge",
6129
6260
  disabled: !canConfirm,
6130
6261
  onPress: handleConfirm,
6131
6262
  style: [fullWidthButtonBase, { flex: 1 }],
6132
- children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Text, { style: { textAlign: "center", color: theme.colors.onPrimary }, children: "Approve Merge" })
6263
+ children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Text, { style: { textAlign: "center", color: theme.colors.onPrimary }, children: "Approve Merge" })
6133
6264
  }
6134
6265
  )
6135
6266
  }
6136
6267
  ),
6137
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native51.View, { style: { height: 8 } }),
6138
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6139
- import_react_native51.View,
6268
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_react_native53.View, { style: { height: 8 } }),
6269
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6270
+ import_react_native53.View,
6140
6271
  {
6141
6272
  style: [
6142
6273
  fullWidthButtonBase,
@@ -6147,22 +6278,22 @@ function ConfirmMergeRequestDialog({
6147
6278
  opacity: isBuilding || !mergeRequest ? 0.5 : 1
6148
6279
  }
6149
6280
  ],
6150
- children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6151
- import_react_native51.Pressable,
6281
+ children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6282
+ import_react_native53.Pressable,
6152
6283
  {
6153
6284
  accessibilityRole: "button",
6154
6285
  accessibilityLabel: isBuilding ? "Preparing\u2026" : "Test edits first",
6155
6286
  disabled: isBuilding || !mergeRequest,
6156
6287
  onPress: handleTestFirst,
6157
6288
  style: [fullWidthButtonBase, { flex: 1 }],
6158
- children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Text, { style: { textAlign: "center", color: theme.colors.text }, children: isBuilding ? "Preparing\u2026" : "Test edits first" })
6289
+ children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Text, { style: { textAlign: "center", color: theme.colors.text }, children: isBuilding ? "Preparing\u2026" : "Test edits first" })
6159
6290
  }
6160
6291
  )
6161
6292
  }
6162
6293
  ),
6163
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native51.View, { style: { height: 8 } }),
6164
- /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6165
- import_react_native51.View,
6294
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(import_react_native53.View, { style: { height: 8 } }),
6295
+ /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6296
+ import_react_native53.View,
6166
6297
  {
6167
6298
  style: [
6168
6299
  fullWidthButtonBase,
@@ -6172,14 +6303,14 @@ function ConfirmMergeRequestDialog({
6172
6303
  borderColor: theme.colors.border
6173
6304
  }
6174
6305
  ],
6175
- children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
6176
- import_react_native51.Pressable,
6306
+ children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6307
+ import_react_native53.Pressable,
6177
6308
  {
6178
6309
  accessibilityRole: "button",
6179
6310
  accessibilityLabel: "Cancel",
6180
6311
  onPress: close,
6181
6312
  style: [fullWidthButtonBase, { flex: 1 }],
6182
- children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Text, { style: { textAlign: "center", color: theme.colors.text }, children: "Cancel" })
6313
+ children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Text, { style: { textAlign: "center", color: theme.colors.text }, children: "Cancel" })
6183
6314
  }
6184
6315
  )
6185
6316
  }
@@ -6191,7 +6322,7 @@ function ConfirmMergeRequestDialog({
6191
6322
  }
6192
6323
 
6193
6324
  // src/studio/ui/ConfirmMergeFlow.tsx
6194
- var import_jsx_runtime54 = require("react/jsx-runtime");
6325
+ var import_jsx_runtime56 = require("react/jsx-runtime");
6195
6326
  function ConfirmMergeFlow({
6196
6327
  visible,
6197
6328
  onOpenChange,
@@ -6202,7 +6333,7 @@ function ConfirmMergeFlow({
6202
6333
  onConfirm,
6203
6334
  onTestFirst
6204
6335
  }) {
6205
- return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
6336
+ return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
6206
6337
  ConfirmMergeRequestDialog,
6207
6338
  {
6208
6339
  visible,
@@ -6223,8 +6354,94 @@ function ConfirmMergeFlow({
6223
6354
  );
6224
6355
  }
6225
6356
 
6357
+ // src/studio/hooks/useOptimisticChatMessages.ts
6358
+ var React40 = __toESM(require("react"));
6359
+ function makeOptimisticId() {
6360
+ return `optimistic:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 10)}`;
6361
+ }
6362
+ function toEpochMs(createdAt) {
6363
+ if (createdAt == null) return 0;
6364
+ if (typeof createdAt === "number") return createdAt;
6365
+ if (createdAt instanceof Date) return createdAt.getTime();
6366
+ const t = Date.parse(String(createdAt));
6367
+ return Number.isFinite(t) ? t : 0;
6368
+ }
6369
+ function isOptimisticResolvedByServer(chatMessages, o) {
6370
+ if (o.failed) return false;
6371
+ const normalize = (s) => s.trim();
6372
+ let startIndex = -1;
6373
+ if (o.baseServerLastId) {
6374
+ startIndex = chatMessages.findIndex((m) => m.id === o.baseServerLastId);
6375
+ }
6376
+ const candidates = startIndex >= 0 ? chatMessages.slice(startIndex + 1) : chatMessages;
6377
+ const target = normalize(o.content);
6378
+ for (const m of candidates) {
6379
+ if (m.author !== "human") continue;
6380
+ if (normalize(m.content) !== target) continue;
6381
+ const serverMs = toEpochMs(m.createdAt);
6382
+ const optimisticMs = Date.parse(o.createdAtIso);
6383
+ if (Number.isFinite(optimisticMs) && optimisticMs > 0 && serverMs > 0) {
6384
+ if (serverMs + 12e4 < optimisticMs) continue;
6385
+ }
6386
+ return true;
6387
+ }
6388
+ return false;
6389
+ }
6390
+ function useOptimisticChatMessages({
6391
+ threadId,
6392
+ shouldForkOnEdit,
6393
+ chatMessages,
6394
+ onSendChat
6395
+ }) {
6396
+ const [optimisticChat, setOptimisticChat] = React40.useState([]);
6397
+ React40.useEffect(() => {
6398
+ setOptimisticChat([]);
6399
+ }, [threadId]);
6400
+ const messages = React40.useMemo(() => {
6401
+ if (!optimisticChat || optimisticChat.length === 0) return chatMessages;
6402
+ const unresolved = optimisticChat.filter((o) => !isOptimisticResolvedByServer(chatMessages, o));
6403
+ if (unresolved.length === 0) return chatMessages;
6404
+ const optimisticAsChat = unresolved.map((o) => ({
6405
+ id: o.id,
6406
+ author: "human",
6407
+ content: o.content,
6408
+ createdAt: o.createdAtIso,
6409
+ kind: "optimistic",
6410
+ meta: o.failed ? { kind: "optimistic", event: "send.failed", status: "error" } : { kind: "optimistic", event: "send.pending", status: "info" }
6411
+ }));
6412
+ const merged = [...chatMessages, ...optimisticAsChat];
6413
+ merged.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
6414
+ return merged;
6415
+ }, [chatMessages, optimisticChat]);
6416
+ React40.useEffect(() => {
6417
+ if (optimisticChat.length === 0) return;
6418
+ setOptimisticChat((prev) => {
6419
+ if (prev.length === 0) return prev;
6420
+ const next = prev.filter((o) => !isOptimisticResolvedByServer(chatMessages, o) || o.failed);
6421
+ return next.length === prev.length ? prev : next;
6422
+ });
6423
+ }, [chatMessages, optimisticChat.length]);
6424
+ const onSend = React40.useCallback(
6425
+ async (text, attachments) => {
6426
+ if (shouldForkOnEdit) {
6427
+ await onSendChat(text, attachments);
6428
+ return;
6429
+ }
6430
+ const createdAtIso = (/* @__PURE__ */ new Date()).toISOString();
6431
+ const baseServerLastId = chatMessages.length > 0 ? chatMessages[chatMessages.length - 1].id : null;
6432
+ const id = makeOptimisticId();
6433
+ setOptimisticChat((prev) => [...prev, { id, content: text, createdAtIso, baseServerLastId, failed: false }]);
6434
+ void Promise.resolve(onSendChat(text, attachments)).catch(() => {
6435
+ setOptimisticChat((prev) => prev.map((m) => m.id === id ? { ...m, failed: true } : m));
6436
+ });
6437
+ },
6438
+ [chatMessages, onSendChat, shouldForkOnEdit]
6439
+ );
6440
+ return { messages, onSend };
6441
+ }
6442
+
6226
6443
  // src/studio/ui/StudioOverlay.tsx
6227
- var import_jsx_runtime55 = require("react/jsx-runtime");
6444
+ var import_jsx_runtime57 = require("react/jsx-runtime");
6228
6445
  function StudioOverlay({
6229
6446
  captureTargetRef,
6230
6447
  app,
@@ -6254,33 +6471,40 @@ function StudioOverlay({
6254
6471
  onNavigateHome
6255
6472
  }) {
6256
6473
  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(
6474
+ const { width } = (0, import_react_native54.useWindowDimensions)();
6475
+ const [sheetOpen, setSheetOpen] = React41.useState(false);
6476
+ const [activePage, setActivePage] = React41.useState("preview");
6477
+ const [drawing, setDrawing] = React41.useState(false);
6478
+ const [chatAttachments, setChatAttachments] = React41.useState([]);
6479
+ const [commentsAppId, setCommentsAppId] = React41.useState(null);
6480
+ const [commentsCount, setCommentsCount] = React41.useState(null);
6481
+ const threadId = (app == null ? void 0 : app.threadId) ?? null;
6482
+ const optimistic = useOptimisticChatMessages({
6483
+ threadId,
6484
+ shouldForkOnEdit,
6485
+ chatMessages,
6486
+ onSendChat
6487
+ });
6488
+ const [confirmMrId, setConfirmMrId] = React41.useState(null);
6489
+ const confirmMr = React41.useMemo(
6266
6490
  () => confirmMrId ? incomingMergeRequests.find((m) => m.id === confirmMrId) ?? null : null,
6267
6491
  [confirmMrId, incomingMergeRequests]
6268
6492
  );
6269
- const handleSheetOpenChange = React38.useCallback((open) => {
6493
+ const handleSheetOpenChange = React41.useCallback((open) => {
6270
6494
  setSheetOpen(open);
6271
- if (!open) import_react_native52.Keyboard.dismiss();
6495
+ if (!open) import_react_native54.Keyboard.dismiss();
6272
6496
  }, []);
6273
- const closeSheet = React38.useCallback(() => {
6497
+ const closeSheet = React41.useCallback(() => {
6274
6498
  handleSheetOpenChange(false);
6275
6499
  }, [handleSheetOpenChange]);
6276
- const openSheet = React38.useCallback(() => setSheetOpen(true), []);
6277
- const goToChat = React38.useCallback(() => {
6500
+ const openSheet = React41.useCallback(() => setSheetOpen(true), []);
6501
+ const goToChat = React41.useCallback(() => {
6278
6502
  setActivePage("chat");
6279
6503
  openSheet();
6280
6504
  }, [openSheet]);
6281
- const backToPreview = React38.useCallback(() => {
6282
- if (import_react_native52.Platform.OS !== "ios") {
6283
- import_react_native52.Keyboard.dismiss();
6505
+ const backToPreview = React41.useCallback(() => {
6506
+ if (import_react_native54.Platform.OS !== "ios") {
6507
+ import_react_native54.Keyboard.dismiss();
6284
6508
  setActivePage("preview");
6285
6509
  return;
6286
6510
  }
@@ -6292,15 +6516,15 @@ function StudioOverlay({
6292
6516
  clearTimeout(t);
6293
6517
  setActivePage("preview");
6294
6518
  };
6295
- const sub = import_react_native52.Keyboard.addListener("keyboardDidHide", finalize);
6519
+ const sub = import_react_native54.Keyboard.addListener("keyboardDidHide", finalize);
6296
6520
  const t = setTimeout(finalize, 350);
6297
- import_react_native52.Keyboard.dismiss();
6521
+ import_react_native54.Keyboard.dismiss();
6298
6522
  }, []);
6299
- const startDraw = React38.useCallback(() => {
6523
+ const startDraw = React41.useCallback(() => {
6300
6524
  setDrawing(true);
6301
6525
  closeSheet();
6302
6526
  }, [closeSheet]);
6303
- const handleDrawCapture = React38.useCallback(
6527
+ const handleDrawCapture = React41.useCallback(
6304
6528
  (dataUrl) => {
6305
6529
  setChatAttachments((prev) => [...prev, dataUrl]);
6306
6530
  setDrawing(false);
@@ -6309,7 +6533,7 @@ function StudioOverlay({
6309
6533
  },
6310
6534
  [openSheet]
6311
6535
  );
6312
- const toggleSheet = React38.useCallback(async () => {
6536
+ const toggleSheet = React41.useCallback(async () => {
6313
6537
  if (!sheetOpen) {
6314
6538
  const shouldExitTest = Boolean(testingMrId) || isTesting;
6315
6539
  if (shouldExitTest) {
@@ -6321,7 +6545,7 @@ function StudioOverlay({
6321
6545
  closeSheet();
6322
6546
  }
6323
6547
  }, [closeSheet, isTesting, onRestoreBase, sheetOpen, testingMrId]);
6324
- const handleTestMr = React38.useCallback(
6548
+ const handleTestMr = React41.useCallback(
6325
6549
  async (mr) => {
6326
6550
  if (!onTestMr) return;
6327
6551
  await onTestMr(mr);
@@ -6329,14 +6553,14 @@ function StudioOverlay({
6329
6553
  },
6330
6554
  [closeSheet, onTestMr]
6331
6555
  );
6332
- return /* @__PURE__ */ (0, import_jsx_runtime55.jsxs)(import_jsx_runtime55.Fragment, { children: [
6333
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(EdgeGlowFrame, { visible: isTesting, role: "accent", thickness: 40, intensity: 1 }),
6334
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(StudioBottomSheet, { open: sheetOpen, onOpenChange: handleSheetOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6556
+ return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
6557
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(EdgeGlowFrame, { visible: isTesting, role: "accent", thickness: 40, intensity: 1 }),
6558
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(StudioBottomSheet, { open: sheetOpen, onOpenChange: handleSheetOpenChange, children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
6335
6559
  StudioSheetPager,
6336
6560
  {
6337
6561
  activePage,
6338
6562
  width,
6339
- preview: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6563
+ preview: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
6340
6564
  PreviewPanel,
6341
6565
  {
6342
6566
  app,
@@ -6362,10 +6586,10 @@ function StudioOverlay({
6362
6586
  commentCountOverride: commentsCount ?? void 0
6363
6587
  }
6364
6588
  ),
6365
- chat: /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6589
+ chat: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
6366
6590
  ChatPanel,
6367
6591
  {
6368
- messages: chatMessages,
6592
+ messages: optimistic.messages,
6369
6593
  showTypingIndicator: chatShowTypingIndicator,
6370
6594
  loading: chatLoading,
6371
6595
  sendDisabled: chatSendDisabled,
@@ -6380,12 +6604,12 @@ function StudioOverlay({
6380
6604
  onClose: closeSheet,
6381
6605
  onNavigateHome,
6382
6606
  onStartDraw: startDraw,
6383
- onSend: onSendChat
6607
+ onSend: optimistic.onSend
6384
6608
  }
6385
6609
  )
6386
6610
  }
6387
6611
  ) }),
6388
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6612
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
6389
6613
  FloatingDraggableButton,
6390
6614
  {
6391
6615
  visible: !sheetOpen && !drawing,
@@ -6393,10 +6617,10 @@ function StudioOverlay({
6393
6617
  badgeCount: incomingMergeRequests.length,
6394
6618
  onPress: toggleSheet,
6395
6619
  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 }) })
6620
+ children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_react_native54.View, { style: { width: 28, height: 28, alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(MergeIcon, { width: 24, height: 24, color: theme.colors.floatingContent }) })
6397
6621
  }
6398
6622
  ),
6399
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6623
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
6400
6624
  DrawModeOverlay,
6401
6625
  {
6402
6626
  visible: drawing,
@@ -6405,7 +6629,7 @@ function StudioOverlay({
6405
6629
  onCapture: handleDrawCapture
6406
6630
  }
6407
6631
  ),
6408
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6632
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
6409
6633
  ConfirmMergeFlow,
6410
6634
  {
6411
6635
  visible: Boolean(confirmMr),
@@ -6418,7 +6642,7 @@ function StudioOverlay({
6418
6642
  onTestFirst: handleTestMr
6419
6643
  }
6420
6644
  ),
6421
- /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
6645
+ /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
6422
6646
  AppCommentsSheet,
6423
6647
  {
6424
6648
  appId: commentsAppId,
@@ -6431,7 +6655,7 @@ function StudioOverlay({
6431
6655
  }
6432
6656
 
6433
6657
  // src/studio/ComergeStudio.tsx
6434
- var import_jsx_runtime56 = require("react/jsx-runtime");
6658
+ var import_jsx_runtime58 = require("react/jsx-runtime");
6435
6659
  function ComergeStudio({
6436
6660
  appId,
6437
6661
  apiKey,
@@ -6439,17 +6663,17 @@ function ComergeStudio({
6439
6663
  onNavigateHome,
6440
6664
  style
6441
6665
  }) {
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(() => {
6666
+ const [activeAppId, setActiveAppId] = React42.useState(appId);
6667
+ const [runtimeAppId, setRuntimeAppId] = React42.useState(appId);
6668
+ const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React42.useState(null);
6669
+ const platform = React42.useMemo(() => import_react_native55.Platform.OS === "ios" ? "ios" : "android", []);
6670
+ React42.useEffect(() => {
6447
6671
  setActiveAppId(appId);
6448
6672
  setRuntimeAppId(appId);
6449
6673
  setPendingRuntimeTargetAppId(null);
6450
6674
  }, [appId]);
6451
- const captureTargetRef = React39.useRef(null);
6452
- 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)(
6675
+ const captureTargetRef = React42.useRef(null);
6676
+ return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(StudioBootstrap, { apiKey, children: ({ userId }) => /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_bottom_sheet6.BottomSheetModalProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(LiquidGlassResetProvider, { resetTriggers: [appId, activeAppId, runtimeAppId], children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
6453
6677
  ComergeStudioInner,
6454
6678
  {
6455
6679
  userId,
@@ -6465,7 +6689,7 @@ function ComergeStudio({
6465
6689
  captureTargetRef,
6466
6690
  style
6467
6691
  }
6468
- ) }) });
6692
+ ) }) }) });
6469
6693
  }
6470
6694
  function ComergeStudioInner({
6471
6695
  userId,
@@ -6484,11 +6708,11 @@ function ComergeStudioInner({
6484
6708
  const { app, loading: appLoading } = useApp(activeAppId);
6485
6709
  const { app: runtimeAppFromHook } = useApp(runtimeAppId, { enabled: runtimeAppId !== activeAppId });
6486
6710
  const runtimeApp = runtimeAppId === activeAppId ? app : runtimeAppFromHook;
6487
- const sawEditingOnPendingTargetRef = React39.useRef(false);
6488
- React39.useEffect(() => {
6711
+ const sawEditingOnPendingTargetRef = React42.useRef(false);
6712
+ React42.useEffect(() => {
6489
6713
  sawEditingOnPendingTargetRef.current = false;
6490
6714
  }, [pendingRuntimeTargetAppId]);
6491
- React39.useEffect(() => {
6715
+ React42.useEffect(() => {
6492
6716
  if (!pendingRuntimeTargetAppId) return;
6493
6717
  if (activeAppId !== pendingRuntimeTargetAppId) return;
6494
6718
  if ((app == null ? void 0 : app.status) === "editing") {
@@ -6505,13 +6729,38 @@ function ComergeStudioInner({
6505
6729
  platform,
6506
6730
  canRequestLatest: (runtimeApp == null ? void 0 : runtimeApp.status) === "ready"
6507
6731
  });
6732
+ const sawEditingOnActiveAppRef = React42.useRef(false);
6733
+ const [showPostEditPreparing, setShowPostEditPreparing] = React42.useState(false);
6734
+ React42.useEffect(() => {
6735
+ sawEditingOnActiveAppRef.current = false;
6736
+ setShowPostEditPreparing(false);
6737
+ }, [activeAppId]);
6738
+ React42.useEffect(() => {
6739
+ if (!(app == null ? void 0 : app.id)) return;
6740
+ if (app.status === "editing") {
6741
+ sawEditingOnActiveAppRef.current = true;
6742
+ setShowPostEditPreparing(false);
6743
+ return;
6744
+ }
6745
+ if (app.status === "ready" && sawEditingOnActiveAppRef.current) {
6746
+ setShowPostEditPreparing(true);
6747
+ sawEditingOnActiveAppRef.current = false;
6748
+ }
6749
+ }, [app == null ? void 0 : app.id, app == null ? void 0 : app.status]);
6750
+ React42.useEffect(() => {
6751
+ if (!showPostEditPreparing) return;
6752
+ const stillProcessingBaseBundle = bundle.loading && bundle.loadingMode === "base" && !bundle.isTesting;
6753
+ if (!stillProcessingBaseBundle) {
6754
+ setShowPostEditPreparing(false);
6755
+ }
6756
+ }, [showPostEditPreparing, bundle.loading, bundle.loadingMode, bundle.isTesting]);
6508
6757
  const threadId = (app == null ? void 0 : app.threadId) ?? "";
6509
6758
  const thread = useThreadMessages(threadId);
6510
6759
  const mergeRequests = useMergeRequests({ appId: activeAppId });
6511
- const hasOpenOutgoingMr = React39.useMemo(() => {
6760
+ const hasOpenOutgoingMr = React42.useMemo(() => {
6512
6761
  return mergeRequests.lists.outgoing.some((mr) => mr.status === "open");
6513
6762
  }, [mergeRequests.lists.outgoing]);
6514
- const incomingReviewMrs = React39.useMemo(() => {
6763
+ const incomingReviewMrs = React42.useMemo(() => {
6515
6764
  if (!userId) return mergeRequests.lists.incoming;
6516
6765
  return mergeRequests.lists.incoming.filter((mr) => mr.createdBy !== userId);
6517
6766
  }, [mergeRequests.lists.incoming, userId]);
@@ -6533,18 +6782,26 @@ function ComergeStudioInner({
6533
6782
  uploadAttachments: uploader.uploadBase64Images
6534
6783
  });
6535
6784
  const chatSendDisabled = hasNoOutcomeAfterLastHuman(thread.raw);
6536
- const [processingMrId, setProcessingMrId] = React39.useState(null);
6537
- const [testingMrId, setTestingMrId] = React39.useState(null);
6538
- const chatShowTypingIndicator = React39.useMemo(() => {
6785
+ const [processingMrId, setProcessingMrId] = React42.useState(null);
6786
+ const [testingMrId, setTestingMrId] = React42.useState(null);
6787
+ const chatShowTypingIndicator = React42.useMemo(() => {
6539
6788
  var _a;
6540
6789
  if (!thread.raw || thread.raw.length === 0) return false;
6541
6790
  const last = thread.raw[thread.raw.length - 1];
6542
6791
  const payloadType = typeof ((_a = last.payload) == null ? void 0 : _a.type) === "string" ? String(last.payload.type) : void 0;
6543
6792
  return payloadType !== "outcome";
6544
6793
  }, [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 }),
6547
- /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
6794
+ return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_react_native55.View, { style: [{ flex: 1 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_react_native55.View, { ref: captureTargetRef, style: { flex: 1 }, collapsable: false, children: [
6795
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
6796
+ RuntimeRenderer,
6797
+ {
6798
+ appKey,
6799
+ bundlePath: bundle.bundlePath,
6800
+ forcePreparing: showPostEditPreparing,
6801
+ renderToken: bundle.renderToken
6802
+ }
6803
+ ),
6804
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
6548
6805
  StudioOverlay,
6549
6806
  {
6550
6807
  captureTargetRef,