@comergehq/studio 0.1.35 → 0.1.37
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 +823 -386
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +832 -387
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
- package/src/components/chat/ChatMessageAttachments.tsx +243 -0
- package/src/components/chat/ChatMessageBubble.tsx +74 -24
- package/src/components/chat/ChatMessageList.tsx +4 -1
- package/src/components/chat/ChatPage.tsx +3 -0
- package/src/components/draw/DrawModeOverlay.tsx +7 -1
- package/src/components/models/types.ts +11 -0
- package/src/components/overlays/EdgeGlowFrame.tsx +7 -2
- package/src/core/services/supabase/client.ts +2 -0
- package/src/data/attachment/types.ts +2 -0
- package/src/studio/ComergeStudio.tsx +3 -0
- package/src/studio/hooks/useAttachmentUpload.ts +37 -6
- package/src/studio/hooks/useOptimisticChatMessages.ts +47 -3
- package/src/studio/hooks/useThreadMessages.ts +79 -5
- package/src/studio/ui/ChatPanel.tsx +3 -0
- package/src/studio/ui/StudioOverlay.tsx +3 -0
- package/src/studio/ui/preview-panel/PreviewRelatedAppsSection.tsx +28 -19
package/dist/index.js
CHANGED
|
@@ -516,8 +516,8 @@ __export(index_exports, {
|
|
|
516
516
|
module.exports = __toCommonJS(index_exports);
|
|
517
517
|
|
|
518
518
|
// src/studio/ComergeStudio.tsx
|
|
519
|
-
var
|
|
520
|
-
var
|
|
519
|
+
var React52 = __toESM(require("react"));
|
|
520
|
+
var import_react_native63 = require("react-native");
|
|
521
521
|
var import_bottom_sheet6 = require("@gorhom/bottom-sheet");
|
|
522
522
|
var import_runtime2 = require("@comergehq/runtime");
|
|
523
523
|
|
|
@@ -730,6 +730,7 @@ publicApi.interceptors.request.use((config) => {
|
|
|
730
730
|
|
|
731
731
|
// src/core/services/supabase/client.ts
|
|
732
732
|
var import_supabase_js = require("@supabase/supabase-js");
|
|
733
|
+
var import_async_storage = __toESM(require("@react-native-async-storage/async-storage"));
|
|
733
734
|
var clientSingleton = null;
|
|
734
735
|
var injectedClient = null;
|
|
735
736
|
var hasInjectedClient = false;
|
|
@@ -756,6 +757,7 @@ function getSupabaseClient() {
|
|
|
756
757
|
}
|
|
757
758
|
clientSingleton = (0, import_supabase_js.createClient)(runtimeConfig.url, runtimeConfig.anonKey, {
|
|
758
759
|
auth: {
|
|
760
|
+
storage: import_async_storage.default,
|
|
759
761
|
autoRefreshToken: true,
|
|
760
762
|
persistSession: true,
|
|
761
763
|
detectSessionInUrl: false
|
|
@@ -1635,6 +1637,34 @@ function compareMessages(a, b) {
|
|
|
1635
1637
|
if (aMs !== bMs) return aMs - bMs;
|
|
1636
1638
|
return String(a.createdAt).localeCompare(String(b.createdAt));
|
|
1637
1639
|
}
|
|
1640
|
+
function parseAttachments(payload) {
|
|
1641
|
+
const raw = payload == null ? void 0 : payload.attachments;
|
|
1642
|
+
if (!Array.isArray(raw)) return [];
|
|
1643
|
+
const out = [];
|
|
1644
|
+
for (const item of raw) {
|
|
1645
|
+
if (!item || typeof item !== "object") continue;
|
|
1646
|
+
const id = typeof item.id === "string" ? String(item.id) : "";
|
|
1647
|
+
const name = typeof item.name === "string" ? String(item.name) : "";
|
|
1648
|
+
const mimeType = typeof item.mimeType === "string" ? String(item.mimeType) : "";
|
|
1649
|
+
const size = typeof item.size === "number" ? Number(item.size) : 0;
|
|
1650
|
+
if (!id || !name || !mimeType || !Number.isFinite(size) || size <= 0) continue;
|
|
1651
|
+
out.push({
|
|
1652
|
+
id,
|
|
1653
|
+
name,
|
|
1654
|
+
mimeType,
|
|
1655
|
+
size,
|
|
1656
|
+
uri: typeof item.downloadUrl === "string" ? String(item.downloadUrl) : void 0,
|
|
1657
|
+
width: typeof item.width === "number" ? Number(item.width) : void 0,
|
|
1658
|
+
height: typeof item.height === "number" ? Number(item.height) : void 0
|
|
1659
|
+
});
|
|
1660
|
+
}
|
|
1661
|
+
return out;
|
|
1662
|
+
}
|
|
1663
|
+
function hasAttachmentWithoutUrl(payload) {
|
|
1664
|
+
const attachments = parseAttachments(payload);
|
|
1665
|
+
if (attachments.length === 0) return false;
|
|
1666
|
+
return attachments.some((att) => !att.uri);
|
|
1667
|
+
}
|
|
1638
1668
|
function mapMessageToChatMessage(m) {
|
|
1639
1669
|
var _a, _b;
|
|
1640
1670
|
const kind = typeof ((_a = m.payload) == null ? void 0 : _a.type) === "string" ? String(m.payload.type) : null;
|
|
@@ -1644,7 +1674,8 @@ function mapMessageToChatMessage(m) {
|
|
|
1644
1674
|
content: typeof ((_b = m.payload) == null ? void 0 : _b.content) === "string" ? m.payload.content : "",
|
|
1645
1675
|
createdAt: m.createdAt,
|
|
1646
1676
|
kind,
|
|
1647
|
-
meta: extractMeta(m.payload)
|
|
1677
|
+
meta: extractMeta(m.payload),
|
|
1678
|
+
attachments: parseAttachments(m.payload)
|
|
1648
1679
|
};
|
|
1649
1680
|
}
|
|
1650
1681
|
function useThreadMessages(threadId) {
|
|
@@ -1655,9 +1686,19 @@ function useThreadMessages(threadId) {
|
|
|
1655
1686
|
const activeRequestIdRef = React4.useRef(0);
|
|
1656
1687
|
const foregroundSignal = useForegroundSignal(Boolean(threadId));
|
|
1657
1688
|
const hasLoadedOnceRef = React4.useRef(false);
|
|
1689
|
+
const attachmentRecoveryTimerRef = React4.useRef(null);
|
|
1690
|
+
const lastAttachmentRecoveryAtRef = React4.useRef(0);
|
|
1658
1691
|
React4.useEffect(() => {
|
|
1659
1692
|
hasLoadedOnceRef.current = false;
|
|
1660
1693
|
}, [threadId]);
|
|
1694
|
+
React4.useEffect(() => {
|
|
1695
|
+
return () => {
|
|
1696
|
+
if (attachmentRecoveryTimerRef.current) {
|
|
1697
|
+
clearTimeout(attachmentRecoveryTimerRef.current);
|
|
1698
|
+
attachmentRecoveryTimerRef.current = null;
|
|
1699
|
+
}
|
|
1700
|
+
};
|
|
1701
|
+
}, []);
|
|
1661
1702
|
const upsertSorted = React4.useCallback((prev, m) => {
|
|
1662
1703
|
const next = prev.filter((x) => x.id !== m.id);
|
|
1663
1704
|
next.push(m);
|
|
@@ -1698,29 +1739,56 @@ function useThreadMessages(threadId) {
|
|
|
1698
1739
|
}
|
|
1699
1740
|
}
|
|
1700
1741
|
}, [threadId]);
|
|
1742
|
+
const recoverAttachmentUrls = React4.useCallback(() => {
|
|
1743
|
+
if (!threadId) return;
|
|
1744
|
+
if (attachmentRecoveryTimerRef.current) return;
|
|
1745
|
+
const now = Date.now();
|
|
1746
|
+
if (now - lastAttachmentRecoveryAtRef.current < 2e3) return;
|
|
1747
|
+
attachmentRecoveryTimerRef.current = setTimeout(() => {
|
|
1748
|
+
attachmentRecoveryTimerRef.current = null;
|
|
1749
|
+
lastAttachmentRecoveryAtRef.current = Date.now();
|
|
1750
|
+
void refetch({ background: true });
|
|
1751
|
+
}, 250);
|
|
1752
|
+
}, [refetch, threadId]);
|
|
1701
1753
|
React4.useEffect(() => {
|
|
1702
1754
|
void refetch();
|
|
1703
1755
|
}, [refetch]);
|
|
1704
1756
|
React4.useEffect(() => {
|
|
1705
1757
|
if (!threadId) return;
|
|
1706
1758
|
const unsubscribe = messagesRepository.subscribeThread(threadId, {
|
|
1707
|
-
onInsert: (m) =>
|
|
1708
|
-
|
|
1759
|
+
onInsert: (m) => {
|
|
1760
|
+
setRaw((prev) => upsertSorted(prev, m));
|
|
1761
|
+
if (hasAttachmentWithoutUrl(m.payload)) {
|
|
1762
|
+
recoverAttachmentUrls();
|
|
1763
|
+
}
|
|
1764
|
+
},
|
|
1765
|
+
onUpdate: (m) => {
|
|
1766
|
+
setRaw((prev) => upsertSorted(prev, m));
|
|
1767
|
+
if (hasAttachmentWithoutUrl(m.payload)) {
|
|
1768
|
+
recoverAttachmentUrls();
|
|
1769
|
+
}
|
|
1770
|
+
},
|
|
1709
1771
|
onDelete: (m) => setRaw((prev) => prev.filter((x) => x.id !== m.id))
|
|
1710
1772
|
});
|
|
1711
1773
|
return unsubscribe;
|
|
1712
|
-
}, [threadId, upsertSorted, foregroundSignal]);
|
|
1774
|
+
}, [threadId, upsertSorted, foregroundSignal, recoverAttachmentUrls]);
|
|
1713
1775
|
React4.useEffect(() => {
|
|
1714
1776
|
if (!threadId) return;
|
|
1715
1777
|
if (foregroundSignal <= 0) return;
|
|
1716
1778
|
void refetch({ background: true });
|
|
1717
1779
|
}, [foregroundSignal, refetch, threadId]);
|
|
1780
|
+
React4.useEffect(() => {
|
|
1781
|
+
if (!threadId) return;
|
|
1782
|
+
if (raw.length === 0) return;
|
|
1783
|
+
if (!raw.some((m) => hasAttachmentWithoutUrl(m.payload))) return;
|
|
1784
|
+
recoverAttachmentUrls();
|
|
1785
|
+
}, [raw, recoverAttachmentUrls, threadId]);
|
|
1718
1786
|
const messages = React4.useMemo(() => {
|
|
1719
1787
|
const visible = raw.filter((m) => !isQueuedHiddenMessage(m));
|
|
1720
1788
|
const resolved = visible.length > 0 ? visible : raw;
|
|
1721
1789
|
return resolved.map(mapMessageToChatMessage);
|
|
1722
1790
|
}, [raw]);
|
|
1723
|
-
return { raw, messages, loading, refreshing, error, refetch };
|
|
1791
|
+
return { raw, messages, loading, refreshing, error, refetch, recoverAttachmentUrls };
|
|
1724
1792
|
}
|
|
1725
1793
|
|
|
1726
1794
|
// src/studio/hooks/useBundleManager.ts
|
|
@@ -2983,6 +3051,23 @@ function getMimeTypeFromDataUrl(dataUrl) {
|
|
|
2983
3051
|
const mimeMatch = header.match(/data:(.*?);base64/i);
|
|
2984
3052
|
return (mimeMatch == null ? void 0 : mimeMatch[1]) ?? "image/png";
|
|
2985
3053
|
}
|
|
3054
|
+
async function getImageDimensionsFromDataUrl(dataUrl) {
|
|
3055
|
+
try {
|
|
3056
|
+
const normalized = dataUrl.startsWith("data:") ? dataUrl : `data:image/png;base64,${dataUrl}`;
|
|
3057
|
+
const dims = await new Promise((resolve, reject) => {
|
|
3058
|
+
import_react_native6.Image.getSize(
|
|
3059
|
+
normalized,
|
|
3060
|
+
(width, height) => resolve({ width, height }),
|
|
3061
|
+
(err) => reject(err)
|
|
3062
|
+
);
|
|
3063
|
+
});
|
|
3064
|
+
if (dims.width > 0 && dims.height > 0) {
|
|
3065
|
+
return { width: Math.round(dims.width), height: Math.round(dims.height) };
|
|
3066
|
+
}
|
|
3067
|
+
} catch {
|
|
3068
|
+
}
|
|
3069
|
+
return {};
|
|
3070
|
+
}
|
|
2986
3071
|
function useAttachmentUpload() {
|
|
2987
3072
|
const [uploading, setUploading] = React7.useState(false);
|
|
2988
3073
|
const [error, setError] = React7.useState(null);
|
|
@@ -2997,17 +3082,24 @@ function useAttachmentUpload() {
|
|
|
2997
3082
|
const normalized = dataUrl.startsWith("data:") ? dataUrl : `data:image/png;base64,${dataUrl}`;
|
|
2998
3083
|
const blob = import_react_native6.Platform.OS === "android" ? await dataUrlToBlobAndroid(normalized) : await (await fetch(normalized)).blob();
|
|
2999
3084
|
const mimeType = getMimeTypeFromDataUrl(normalized);
|
|
3000
|
-
|
|
3085
|
+
const dimensions = mimeType.startsWith("image/") ? await getImageDimensionsFromDataUrl(normalized) : {};
|
|
3086
|
+
return { blob, idx, mimeType, ...dimensions };
|
|
3001
3087
|
})
|
|
3002
3088
|
);
|
|
3003
|
-
const files = blobs.map(({ blob, mimeType }, idx) => ({
|
|
3089
|
+
const files = blobs.map(({ blob, mimeType, width, height }, idx) => ({
|
|
3004
3090
|
name: `attachment-${Date.now()}-${idx}.png`,
|
|
3005
3091
|
size: blob.size,
|
|
3006
|
-
mimeType
|
|
3092
|
+
mimeType,
|
|
3093
|
+
width,
|
|
3094
|
+
height
|
|
3007
3095
|
}));
|
|
3008
3096
|
const presign = await attachmentRepository.presign({ threadId, appId, files });
|
|
3009
3097
|
await Promise.all(presign.uploads.map((u, index) => attachmentRepository.upload(u, blobs[index].blob)));
|
|
3010
|
-
return presign.uploads.map((u) =>
|
|
3098
|
+
return presign.uploads.map((u, index) => ({
|
|
3099
|
+
...u.attachment,
|
|
3100
|
+
width: blobs[index].width,
|
|
3101
|
+
height: blobs[index].height
|
|
3102
|
+
}));
|
|
3011
3103
|
} catch (e) {
|
|
3012
3104
|
const err = e instanceof Error ? e : new Error(String(e));
|
|
3013
3105
|
setError(err);
|
|
@@ -3026,13 +3118,16 @@ function useAttachmentUpload() {
|
|
|
3026
3118
|
const normalized = dataUrl.startsWith("data:") ? dataUrl : `data:image/png;base64,${dataUrl}`;
|
|
3027
3119
|
const blob = import_react_native6.Platform.OS === "android" ? await dataUrlToBlobAndroid(normalized) : await (await fetch(normalized)).blob();
|
|
3028
3120
|
const mimeType = getMimeTypeFromDataUrl(normalized);
|
|
3029
|
-
|
|
3121
|
+
const dimensions = mimeType.startsWith("image/") ? await getImageDimensionsFromDataUrl(normalized) : {};
|
|
3122
|
+
return { blob, mimeType, ...dimensions };
|
|
3030
3123
|
})
|
|
3031
3124
|
);
|
|
3032
|
-
const files = blobs.map(({ blob, mimeType }, idx) => ({
|
|
3125
|
+
const files = blobs.map(({ blob, mimeType, width, height }, idx) => ({
|
|
3033
3126
|
name: `attachment-${Date.now()}-${idx}.png`,
|
|
3034
3127
|
size: blob.size,
|
|
3035
|
-
mimeType
|
|
3128
|
+
mimeType,
|
|
3129
|
+
width,
|
|
3130
|
+
height
|
|
3036
3131
|
}));
|
|
3037
3132
|
const presign = await attachmentRepository.stagePresign({ files });
|
|
3038
3133
|
await Promise.all(presign.uploads.map((u, index) => attachmentRepository.uploadStaged(u, blobs[index].blob)));
|
|
@@ -3315,8 +3410,8 @@ function RuntimeRenderer({
|
|
|
3315
3410
|
}
|
|
3316
3411
|
|
|
3317
3412
|
// src/studio/ui/StudioOverlay.tsx
|
|
3318
|
-
var
|
|
3319
|
-
var
|
|
3413
|
+
var React47 = __toESM(require("react"));
|
|
3414
|
+
var import_react_native62 = require("react-native");
|
|
3320
3415
|
|
|
3321
3416
|
// src/components/studio-sheet/StudioBottomSheet.tsx
|
|
3322
3417
|
var React12 = __toESM(require("react"));
|
|
@@ -3856,6 +3951,7 @@ function EdgeGlowFrame({
|
|
|
3856
3951
|
role = "accent",
|
|
3857
3952
|
thickness = 40,
|
|
3858
3953
|
intensity = 1,
|
|
3954
|
+
animationDurationMs = 300,
|
|
3859
3955
|
style
|
|
3860
3956
|
}) {
|
|
3861
3957
|
const theme = useTheme();
|
|
@@ -3864,10 +3960,10 @@ function EdgeGlowFrame({
|
|
|
3864
3960
|
React14.useEffect(() => {
|
|
3865
3961
|
import_react_native13.Animated.timing(anim, {
|
|
3866
3962
|
toValue: visible ? 1 : 0,
|
|
3867
|
-
duration:
|
|
3963
|
+
duration: animationDurationMs,
|
|
3868
3964
|
useNativeDriver: true
|
|
3869
3965
|
}).start();
|
|
3870
|
-
}, [anim, visible]);
|
|
3966
|
+
}, [anim, visible, animationDurationMs]);
|
|
3871
3967
|
const c = baseColor(role, theme);
|
|
3872
3968
|
const strong = withAlpha(c, 0.6 * alpha);
|
|
3873
3969
|
const soft = withAlpha(c, 0.22 * alpha);
|
|
@@ -4386,7 +4482,16 @@ function DrawModeOverlay({
|
|
|
4386
4482
|
}, [captureTargetRef, capturing, onCapture]);
|
|
4387
4483
|
if (!visible) return null;
|
|
4388
4484
|
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_react_native17.View, { style: [import_react_native17.StyleSheet.absoluteFill, styles3.root, style], pointerEvents: "box-none", children: [
|
|
4389
|
-
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4485
|
+
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4486
|
+
EdgeGlowFrame,
|
|
4487
|
+
{
|
|
4488
|
+
visible: !hideUi,
|
|
4489
|
+
role: "danger",
|
|
4490
|
+
thickness: 50,
|
|
4491
|
+
intensity: 1,
|
|
4492
|
+
animationDurationMs: hideUi ? 0 : 300
|
|
4493
|
+
}
|
|
4494
|
+
),
|
|
4390
4495
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
|
|
4391
4496
|
DrawSurface,
|
|
4392
4497
|
{
|
|
@@ -5874,7 +5979,10 @@ function PreviewRelatedAppsSection({
|
|
|
5874
5979
|
}) {
|
|
5875
5980
|
var _a;
|
|
5876
5981
|
const theme = useTheme();
|
|
5982
|
+
const { height: windowHeight } = (0, import_react_native35.useWindowDimensions)();
|
|
5877
5983
|
const [relatedAppsOpen, setRelatedAppsOpen] = React27.useState(false);
|
|
5984
|
+
const modalMaxHeight = Math.max(240, windowHeight * 0.5);
|
|
5985
|
+
const modalScrollMaxHeight = Math.max(140, modalMaxHeight - 96);
|
|
5878
5986
|
const relatedAppItems = React27.useMemo(() => {
|
|
5879
5987
|
if (!relatedApps) return [];
|
|
5880
5988
|
const items = [];
|
|
@@ -6007,10 +6115,7 @@ function PreviewRelatedAppsSection({
|
|
|
6007
6115
|
}
|
|
6008
6116
|
)
|
|
6009
6117
|
] }),
|
|
6010
|
-
/* @__PURE__ */ (0, import_jsx_runtime36.
|
|
6011
|
-
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_native35.View, { style: { minHeight: 20, justifyContent: "center" }, children: item.app.status !== "ready" ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(PreviewStatusBadge, { status: item.app.status }) : null }),
|
|
6012
|
-
isSwitching ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_native35.ActivityIndicator, { size: "small", color: theme.colors.primary }) : null
|
|
6013
|
-
] })
|
|
6118
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_native35.View, { style: { alignItems: "flex-end", gap: 6 }, children: isSwitching ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_react_native35.ActivityIndicator, { size: "small", color: theme.colors.primary }) : null })
|
|
6014
6119
|
] })
|
|
6015
6120
|
},
|
|
6016
6121
|
item.app.id
|
|
@@ -6053,17 +6158,35 @@ function PreviewRelatedAppsSection({
|
|
|
6053
6158
|
children: inlineItems.map((item) => renderRelatedCard(item))
|
|
6054
6159
|
}
|
|
6055
6160
|
),
|
|
6056
|
-
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
6057
|
-
|
|
6058
|
-
|
|
6059
|
-
|
|
6060
|
-
|
|
6061
|
-
|
|
6062
|
-
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6161
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
6162
|
+
Modal,
|
|
6163
|
+
{
|
|
6164
|
+
visible: relatedAppsOpen,
|
|
6165
|
+
onRequestClose: closeRelatedApps,
|
|
6166
|
+
contentStyle: { maxHeight: modalMaxHeight, overflow: "hidden" },
|
|
6167
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_react_native35.View, { style: { gap: theme.spacing.sm, minHeight: 0 }, children: [
|
|
6168
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Text, { style: { color: theme.colors.text, fontSize: 18, fontWeight: theme.typography.fontWeight.semibold }, children: "Related apps" }),
|
|
6169
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
6170
|
+
import_react_native35.ScrollView,
|
|
6171
|
+
{
|
|
6172
|
+
style: { maxHeight: modalScrollMaxHeight },
|
|
6173
|
+
contentContainerStyle: { paddingBottom: theme.spacing.xs, gap: theme.spacing.sm },
|
|
6174
|
+
showsVerticalScrollIndicator: true,
|
|
6175
|
+
children: [
|
|
6176
|
+
sectionedRelatedApps.original.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_react_native35.View, { children: [
|
|
6177
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Text, { style: { color: theme.colors.textMuted, marginBottom: theme.spacing.xs }, children: "Original" }),
|
|
6178
|
+
sectionedRelatedApps.original.map((item) => renderRelatedCard(item, { fullWidth: true }))
|
|
6179
|
+
] }) : null,
|
|
6180
|
+
sectionedRelatedApps.remixes.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(import_react_native35.View, { children: [
|
|
6181
|
+
/* @__PURE__ */ (0, import_jsx_runtime36.jsx)(Text, { style: { color: theme.colors.textMuted, marginBottom: theme.spacing.xs }, children: "Remixes" }),
|
|
6182
|
+
sectionedRelatedApps.remixes.map((item) => renderRelatedCard(item, { fullWidth: true }))
|
|
6183
|
+
] }) : null
|
|
6184
|
+
]
|
|
6185
|
+
}
|
|
6186
|
+
)
|
|
6187
|
+
] })
|
|
6188
|
+
}
|
|
6189
|
+
)
|
|
6067
6190
|
] });
|
|
6068
6191
|
}
|
|
6069
6192
|
|
|
@@ -7636,22 +7759,22 @@ ${shareUrl}`;
|
|
|
7636
7759
|
}
|
|
7637
7760
|
|
|
7638
7761
|
// src/studio/ui/ChatPanel.tsx
|
|
7639
|
-
var
|
|
7640
|
-
var
|
|
7762
|
+
var React44 = __toESM(require("react"));
|
|
7763
|
+
var import_react_native59 = require("react-native");
|
|
7641
7764
|
|
|
7642
7765
|
// src/components/chat/ChatPage.tsx
|
|
7643
|
-
var
|
|
7644
|
-
var
|
|
7766
|
+
var React41 = __toESM(require("react"));
|
|
7767
|
+
var import_react_native52 = require("react-native");
|
|
7645
7768
|
var import_react_native_safe_area_context4 = require("react-native-safe-area-context");
|
|
7646
7769
|
|
|
7647
7770
|
// src/components/chat/ChatMessageList.tsx
|
|
7648
|
-
var
|
|
7649
|
-
var
|
|
7771
|
+
var React40 = __toESM(require("react"));
|
|
7772
|
+
var import_react_native51 = require("react-native");
|
|
7650
7773
|
var import_bottom_sheet5 = require("@gorhom/bottom-sheet");
|
|
7651
7774
|
|
|
7652
7775
|
// src/components/chat/ChatMessageBubble.tsx
|
|
7653
|
-
var
|
|
7654
|
-
var
|
|
7776
|
+
var React38 = __toESM(require("react"));
|
|
7777
|
+
var import_react_native49 = require("react-native");
|
|
7655
7778
|
var import_lucide_react_native10 = require("lucide-react-native");
|
|
7656
7779
|
|
|
7657
7780
|
// src/components/primitives/Button.tsx
|
|
@@ -7717,19 +7840,261 @@ function Button({
|
|
|
7717
7840
|
);
|
|
7718
7841
|
}
|
|
7719
7842
|
|
|
7720
|
-
// src/components/chat/
|
|
7843
|
+
// src/components/chat/ChatMessageAttachments.tsx
|
|
7844
|
+
var React37 = __toESM(require("react"));
|
|
7845
|
+
var import_react_native48 = require("react-native");
|
|
7846
|
+
var import_expo_image = require("expo-image");
|
|
7721
7847
|
var import_jsx_runtime49 = require("react/jsx-runtime");
|
|
7848
|
+
function ChatMessageAttachments({
|
|
7849
|
+
messageId,
|
|
7850
|
+
attachments,
|
|
7851
|
+
align = "left",
|
|
7852
|
+
onAttachmentLoadError
|
|
7853
|
+
}) {
|
|
7854
|
+
const theme = useTheme();
|
|
7855
|
+
const [viewerVisible, setViewerVisible] = React37.useState(false);
|
|
7856
|
+
const [viewerIndex, setViewerIndex] = React37.useState(0);
|
|
7857
|
+
const failedKeysRef = React37.useRef(/* @__PURE__ */ new Set());
|
|
7858
|
+
const [loadingById, setLoadingById] = React37.useState({});
|
|
7859
|
+
const [modalLoadingById, setModalLoadingById] = React37.useState({});
|
|
7860
|
+
const pulse = React37.useRef(new import_react_native48.Animated.Value(0.45)).current;
|
|
7861
|
+
const imageAttachments = React37.useMemo(
|
|
7862
|
+
() => attachments.filter(
|
|
7863
|
+
(att) => att.mimeType.startsWith("image/") && typeof att.uri === "string" && att.uri.length > 0
|
|
7864
|
+
),
|
|
7865
|
+
[attachments]
|
|
7866
|
+
);
|
|
7867
|
+
const itemHeight = imageAttachments.length === 1 ? 180 : 124;
|
|
7868
|
+
const maxItemWidth = imageAttachments.length === 1 ? 280 : 180;
|
|
7869
|
+
const getAspectRatio = (att) => {
|
|
7870
|
+
const width = typeof att.width === "number" ? att.width : 0;
|
|
7871
|
+
const height = typeof att.height === "number" ? att.height : 0;
|
|
7872
|
+
if (width > 0 && height > 0) {
|
|
7873
|
+
return Math.max(0.35, Math.min(2.4, width / height));
|
|
7874
|
+
}
|
|
7875
|
+
return 0.8;
|
|
7876
|
+
};
|
|
7877
|
+
React37.useEffect(() => {
|
|
7878
|
+
import_react_native48.Animated.loop(
|
|
7879
|
+
import_react_native48.Animated.sequence([
|
|
7880
|
+
import_react_native48.Animated.timing(pulse, { toValue: 0.85, duration: 650, useNativeDriver: true }),
|
|
7881
|
+
import_react_native48.Animated.timing(pulse, { toValue: 0.45, duration: 650, useNativeDriver: true })
|
|
7882
|
+
])
|
|
7883
|
+
).start();
|
|
7884
|
+
}, [pulse]);
|
|
7885
|
+
React37.useEffect(() => {
|
|
7886
|
+
if (imageAttachments.length === 0) {
|
|
7887
|
+
setLoadingById({});
|
|
7888
|
+
setModalLoadingById({});
|
|
7889
|
+
return;
|
|
7890
|
+
}
|
|
7891
|
+
setLoadingById((prev) => {
|
|
7892
|
+
const next = {};
|
|
7893
|
+
for (const att of imageAttachments) {
|
|
7894
|
+
next[att.id] = prev[att.id] ?? true;
|
|
7895
|
+
}
|
|
7896
|
+
return next;
|
|
7897
|
+
});
|
|
7898
|
+
}, [imageAttachments]);
|
|
7899
|
+
React37.useEffect(() => {
|
|
7900
|
+
if (!viewerVisible) return;
|
|
7901
|
+
if (imageAttachments.length === 0) {
|
|
7902
|
+
setModalLoadingById({});
|
|
7903
|
+
return;
|
|
7904
|
+
}
|
|
7905
|
+
setModalLoadingById(() => {
|
|
7906
|
+
const next = {};
|
|
7907
|
+
for (const att of imageAttachments) {
|
|
7908
|
+
next[att.id] = true;
|
|
7909
|
+
}
|
|
7910
|
+
return next;
|
|
7911
|
+
});
|
|
7912
|
+
}, [viewerVisible, imageAttachments]);
|
|
7913
|
+
if (imageAttachments.length === 0) return null;
|
|
7914
|
+
const handleError = (attachmentId) => {
|
|
7915
|
+
const key = `${messageId}:${attachmentId}`;
|
|
7916
|
+
if (failedKeysRef.current.has(key)) return;
|
|
7917
|
+
failedKeysRef.current.add(key);
|
|
7918
|
+
onAttachmentLoadError == null ? void 0 : onAttachmentLoadError(messageId, attachmentId);
|
|
7919
|
+
};
|
|
7920
|
+
return /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_jsx_runtime49.Fragment, { children: [
|
|
7921
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
7922
|
+
import_react_native48.View,
|
|
7923
|
+
{
|
|
7924
|
+
style: {
|
|
7925
|
+
flexDirection: "row",
|
|
7926
|
+
flexWrap: "wrap",
|
|
7927
|
+
justifyContent: align === "right" ? "flex-end" : "flex-start",
|
|
7928
|
+
alignSelf: align === "right" ? "flex-end" : "flex-start",
|
|
7929
|
+
gap: theme.spacing.sm,
|
|
7930
|
+
marginBottom: theme.spacing.sm
|
|
7931
|
+
},
|
|
7932
|
+
children: imageAttachments.map((att, index) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
7933
|
+
import_react_native48.Pressable,
|
|
7934
|
+
{
|
|
7935
|
+
onPress: () => {
|
|
7936
|
+
setViewerIndex(index);
|
|
7937
|
+
setViewerVisible(true);
|
|
7938
|
+
},
|
|
7939
|
+
accessibilityRole: "button",
|
|
7940
|
+
accessibilityLabel: `Attachment ${index + 1} of ${imageAttachments.length}`,
|
|
7941
|
+
style: {
|
|
7942
|
+
height: itemHeight,
|
|
7943
|
+
aspectRatio: getAspectRatio(att),
|
|
7944
|
+
maxWidth: maxItemWidth,
|
|
7945
|
+
borderRadius: theme.radii.lg,
|
|
7946
|
+
overflow: "hidden"
|
|
7947
|
+
},
|
|
7948
|
+
children: [
|
|
7949
|
+
loadingById[att.id] ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
7950
|
+
import_react_native48.Animated.View,
|
|
7951
|
+
{
|
|
7952
|
+
style: {
|
|
7953
|
+
...import_react_native48.StyleSheet.absoluteFillObject,
|
|
7954
|
+
opacity: pulse,
|
|
7955
|
+
backgroundColor: theme.colors.border
|
|
7956
|
+
}
|
|
7957
|
+
}
|
|
7958
|
+
) : null,
|
|
7959
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
7960
|
+
import_expo_image.Image,
|
|
7961
|
+
{
|
|
7962
|
+
source: { uri: att.uri },
|
|
7963
|
+
style: { width: "100%", height: "100%" },
|
|
7964
|
+
contentFit: "contain",
|
|
7965
|
+
transition: 140,
|
|
7966
|
+
cachePolicy: "memory-disk",
|
|
7967
|
+
onLoadStart: () => {
|
|
7968
|
+
setLoadingById((prev) => ({ ...prev, [att.id]: true }));
|
|
7969
|
+
},
|
|
7970
|
+
onLoadEnd: () => {
|
|
7971
|
+
setLoadingById((prev) => ({ ...prev, [att.id]: false }));
|
|
7972
|
+
},
|
|
7973
|
+
onError: () => handleError(att.id)
|
|
7974
|
+
}
|
|
7975
|
+
)
|
|
7976
|
+
]
|
|
7977
|
+
},
|
|
7978
|
+
att.id
|
|
7979
|
+
))
|
|
7980
|
+
}
|
|
7981
|
+
),
|
|
7982
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(import_react_native48.Modal, { visible: viewerVisible, transparent: true, animationType: "fade", onRequestClose: () => setViewerVisible(false), children: /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_react_native48.View, { style: { flex: 1, backgroundColor: "rgba(0,0,0,0.95)" }, children: [
|
|
7983
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
7984
|
+
import_react_native48.View,
|
|
7985
|
+
{
|
|
7986
|
+
style: {
|
|
7987
|
+
position: "absolute",
|
|
7988
|
+
top: 56,
|
|
7989
|
+
right: 16,
|
|
7990
|
+
zIndex: 2
|
|
7991
|
+
},
|
|
7992
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
7993
|
+
import_react_native48.Pressable,
|
|
7994
|
+
{
|
|
7995
|
+
accessibilityRole: "button",
|
|
7996
|
+
accessibilityLabel: "Close attachment viewer",
|
|
7997
|
+
onPress: () => setViewerVisible(false),
|
|
7998
|
+
style: {
|
|
7999
|
+
width: 44,
|
|
8000
|
+
height: 44,
|
|
8001
|
+
borderRadius: 22,
|
|
8002
|
+
alignItems: "center",
|
|
8003
|
+
justifyContent: "center",
|
|
8004
|
+
backgroundColor: "rgba(255,255,255,0.15)"
|
|
8005
|
+
},
|
|
8006
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(IconClose, { size: 18, colorToken: "onPrimary" })
|
|
8007
|
+
}
|
|
8008
|
+
)
|
|
8009
|
+
}
|
|
8010
|
+
),
|
|
8011
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
8012
|
+
import_react_native48.FlatList,
|
|
8013
|
+
{
|
|
8014
|
+
data: imageAttachments,
|
|
8015
|
+
horizontal: true,
|
|
8016
|
+
pagingEnabled: true,
|
|
8017
|
+
initialScrollIndex: viewerIndex,
|
|
8018
|
+
keyExtractor: (item) => item.id,
|
|
8019
|
+
showsHorizontalScrollIndicator: false,
|
|
8020
|
+
getItemLayout: (_, index) => {
|
|
8021
|
+
const width = import_react_native48.Dimensions.get("window").width;
|
|
8022
|
+
return { length: width, offset: width * index, index };
|
|
8023
|
+
},
|
|
8024
|
+
renderItem: ({ item, index }) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(import_react_native48.View, { style: { width: import_react_native48.Dimensions.get("window").width, height: "100%", justifyContent: "center" }, children: [
|
|
8025
|
+
modalLoadingById[item.id] ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
8026
|
+
import_react_native48.Animated.View,
|
|
8027
|
+
{
|
|
8028
|
+
style: {
|
|
8029
|
+
...import_react_native48.StyleSheet.absoluteFillObject,
|
|
8030
|
+
opacity: pulse,
|
|
8031
|
+
backgroundColor: theme.colors.border
|
|
8032
|
+
}
|
|
8033
|
+
}
|
|
8034
|
+
) : null,
|
|
8035
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
|
|
8036
|
+
import_expo_image.Image,
|
|
8037
|
+
{
|
|
8038
|
+
source: { uri: item.uri },
|
|
8039
|
+
style: { width: "100%", height: "78%" },
|
|
8040
|
+
contentFit: "contain",
|
|
8041
|
+
transition: 140,
|
|
8042
|
+
cachePolicy: "memory-disk",
|
|
8043
|
+
onLoadStart: () => {
|
|
8044
|
+
setModalLoadingById((prev) => ({ ...prev, [item.id]: true }));
|
|
8045
|
+
},
|
|
8046
|
+
onLoadEnd: () => {
|
|
8047
|
+
setModalLoadingById((prev) => ({ ...prev, [item.id]: false }));
|
|
8048
|
+
},
|
|
8049
|
+
onError: () => handleError(item.id)
|
|
8050
|
+
}
|
|
8051
|
+
),
|
|
8052
|
+
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(
|
|
8053
|
+
Text,
|
|
8054
|
+
{
|
|
8055
|
+
variant: "caption",
|
|
8056
|
+
color: "#FFFFFF",
|
|
8057
|
+
style: { textAlign: "center", marginTop: theme.spacing.sm },
|
|
8058
|
+
children: [
|
|
8059
|
+
index + 1,
|
|
8060
|
+
" / ",
|
|
8061
|
+
imageAttachments.length
|
|
8062
|
+
]
|
|
8063
|
+
}
|
|
8064
|
+
)
|
|
8065
|
+
] })
|
|
8066
|
+
}
|
|
8067
|
+
)
|
|
8068
|
+
] }) })
|
|
8069
|
+
] });
|
|
8070
|
+
}
|
|
8071
|
+
|
|
8072
|
+
// src/components/chat/ChatMessageBubble.tsx
|
|
8073
|
+
var import_jsx_runtime50 = require("react/jsx-runtime");
|
|
7722
8074
|
function areMessageMetaEqual(a, b) {
|
|
7723
8075
|
if (a === b) return true;
|
|
7724
8076
|
if (!a || !b) return a === b;
|
|
7725
8077
|
return a.kind === b.kind && a.event === b.event && a.status === b.status && a.mergeRequestId === b.mergeRequestId && a.sourceAppId === b.sourceAppId && a.targetAppId === b.targetAppId && a.appId === b.appId && a.threadId === b.threadId;
|
|
7726
8078
|
}
|
|
8079
|
+
function areMessageAttachmentsEqual(a, b) {
|
|
8080
|
+
if (a === b) return true;
|
|
8081
|
+
const left = a ?? [];
|
|
8082
|
+
const right = b ?? [];
|
|
8083
|
+
if (left.length !== right.length) return false;
|
|
8084
|
+
for (let i = 0; i < left.length; i += 1) {
|
|
8085
|
+
if (left[i].id !== right[i].id || left[i].name !== right[i].name || left[i].mimeType !== right[i].mimeType || left[i].size !== right[i].size || left[i].uri !== right[i].uri || left[i].width !== right[i].width || left[i].height !== right[i].height) {
|
|
8086
|
+
return false;
|
|
8087
|
+
}
|
|
8088
|
+
}
|
|
8089
|
+
return true;
|
|
8090
|
+
}
|
|
7727
8091
|
function ChatMessageBubbleInner({
|
|
7728
8092
|
message,
|
|
7729
8093
|
renderContent,
|
|
7730
8094
|
isLast,
|
|
7731
8095
|
retrying,
|
|
7732
8096
|
onRetryMessage,
|
|
8097
|
+
onAttachmentLoadError,
|
|
7733
8098
|
style
|
|
7734
8099
|
}) {
|
|
7735
8100
|
var _a, _b;
|
|
@@ -7748,11 +8113,36 @@ function ChatMessageBubbleInner({
|
|
|
7748
8113
|
const bodyColor = metaStatus === "success" ? theme.colors.success : metaStatus === "error" ? theme.colors.danger : void 0;
|
|
7749
8114
|
const showRetry = Boolean(onRetryMessage) && isLast && metaStatus === "error" && message.author === "human";
|
|
7750
8115
|
const retryLabel = retrying ? "Retrying..." : "Retry";
|
|
7751
|
-
const
|
|
8116
|
+
const hasText = message.content.trim().length > 0;
|
|
8117
|
+
const attachments = message.attachments ?? [];
|
|
8118
|
+
const hasAttachments = attachments.length > 0;
|
|
8119
|
+
const hasStatusIcon = isMergeCompleted || isSyncCompleted || isMergeApproved || isSyncStarted;
|
|
8120
|
+
const shouldRenderBubble = hasText || hasStatusIcon;
|
|
8121
|
+
const handleRetryPress = React38.useCallback(() => {
|
|
7752
8122
|
onRetryMessage == null ? void 0 : onRetryMessage(message.id);
|
|
7753
8123
|
}, [message.id, onRetryMessage]);
|
|
7754
|
-
return /* @__PURE__ */ (0,
|
|
7755
|
-
/* @__PURE__ */ (0,
|
|
8124
|
+
return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_react_native49.View, { style: [align, style], children: [
|
|
8125
|
+
hasAttachments ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
8126
|
+
import_react_native49.View,
|
|
8127
|
+
{
|
|
8128
|
+
style: {
|
|
8129
|
+
maxWidth: "85%",
|
|
8130
|
+
marginBottom: shouldRenderBubble ? theme.spacing.xs : 0,
|
|
8131
|
+
alignSelf: isHuman ? "flex-end" : "flex-start",
|
|
8132
|
+
alignItems: isHuman ? "flex-end" : "flex-start"
|
|
8133
|
+
},
|
|
8134
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
8135
|
+
ChatMessageAttachments,
|
|
8136
|
+
{
|
|
8137
|
+
messageId: message.id,
|
|
8138
|
+
attachments,
|
|
8139
|
+
align: isHuman ? "right" : "left",
|
|
8140
|
+
onAttachmentLoadError
|
|
8141
|
+
}
|
|
8142
|
+
)
|
|
8143
|
+
}
|
|
8144
|
+
) : null,
|
|
8145
|
+
shouldRenderBubble ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
7756
8146
|
Surface,
|
|
7757
8147
|
{
|
|
7758
8148
|
variant: bubbleVariant,
|
|
@@ -7767,14 +8157,14 @@ function ChatMessageBubbleInner({
|
|
|
7767
8157
|
},
|
|
7768
8158
|
cornerStyle
|
|
7769
8159
|
],
|
|
7770
|
-
children: /* @__PURE__ */ (0,
|
|
7771
|
-
isMergeCompleted || isSyncCompleted ? /* @__PURE__ */ (0,
|
|
7772
|
-
isMergeApproved || isSyncStarted ? /* @__PURE__ */ (0,
|
|
7773
|
-
/* @__PURE__ */ (0,
|
|
8160
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_react_native49.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
|
|
8161
|
+
isMergeCompleted || isSyncCompleted ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_lucide_react_native10.CheckCheck, { size: 16, color: theme.colors.success, style: { marginRight: theme.spacing.sm } }) : null,
|
|
8162
|
+
isMergeApproved || isSyncStarted ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_lucide_react_native10.GitMerge, { size: 16, color: theme.colors.text, style: { marginRight: theme.spacing.sm } }) : null,
|
|
8163
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_react_native49.View, { style: { flexShrink: 1, minWidth: 0 }, children: renderContent ? renderContent(message) : /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(MarkdownText, { markdown: message.content, variant: "chat", bodyColor }) })
|
|
7774
8164
|
] })
|
|
7775
8165
|
}
|
|
7776
|
-
),
|
|
7777
|
-
showRetry ? /* @__PURE__ */ (0,
|
|
8166
|
+
) : null,
|
|
8167
|
+
showRetry ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_react_native49.View, { style: { marginTop: theme.spacing.xs, alignSelf: align.alignSelf }, children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
7778
8168
|
Button,
|
|
7779
8169
|
{
|
|
7780
8170
|
variant: "ghost",
|
|
@@ -7783,9 +8173,9 @@ function ChatMessageBubbleInner({
|
|
|
7783
8173
|
disabled: retrying,
|
|
7784
8174
|
style: { borderColor: theme.colors.danger },
|
|
7785
8175
|
accessibilityLabel: "Retry send",
|
|
7786
|
-
children: /* @__PURE__ */ (0,
|
|
7787
|
-
!retrying ? /* @__PURE__ */ (0,
|
|
7788
|
-
/* @__PURE__ */ (0,
|
|
8176
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(import_react_native49.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
|
|
8177
|
+
!retrying ? /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(import_lucide_react_native10.RotateCcw, { size: 14, color: theme.colors.danger }) : null,
|
|
8178
|
+
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(
|
|
7789
8179
|
Text,
|
|
7790
8180
|
{
|
|
7791
8181
|
variant: "caption",
|
|
@@ -7801,30 +8191,30 @@ function ChatMessageBubbleInner({
|
|
|
7801
8191
|
] });
|
|
7802
8192
|
}
|
|
7803
8193
|
function areEqual(prev, next) {
|
|
7804
|
-
return prev.message.id === next.message.id && prev.message.author === next.message.author && prev.message.content === next.message.content && prev.message.kind === next.message.kind && String(prev.message.createdAt) === String(next.message.createdAt) && areMessageMetaEqual(prev.message.meta, next.message.meta) && prev.renderContent === next.renderContent && prev.isLast === next.isLast && prev.retrying === next.retrying && prev.onRetryMessage === next.onRetryMessage && prev.style === next.style;
|
|
8194
|
+
return prev.message.id === next.message.id && prev.message.author === next.message.author && prev.message.content === next.message.content && areMessageAttachmentsEqual(prev.message.attachments, next.message.attachments) && prev.message.kind === next.message.kind && String(prev.message.createdAt) === String(next.message.createdAt) && areMessageMetaEqual(prev.message.meta, next.message.meta) && prev.renderContent === next.renderContent && prev.isLast === next.isLast && prev.retrying === next.retrying && prev.onRetryMessage === next.onRetryMessage && prev.onAttachmentLoadError === next.onAttachmentLoadError && prev.style === next.style;
|
|
7805
8195
|
}
|
|
7806
|
-
var ChatMessageBubble =
|
|
8196
|
+
var ChatMessageBubble = React38.memo(ChatMessageBubbleInner, areEqual);
|
|
7807
8197
|
ChatMessageBubble.displayName = "ChatMessageBubble";
|
|
7808
8198
|
|
|
7809
8199
|
// src/components/chat/TypingIndicator.tsx
|
|
7810
|
-
var
|
|
7811
|
-
var
|
|
7812
|
-
var
|
|
8200
|
+
var React39 = __toESM(require("react"));
|
|
8201
|
+
var import_react_native50 = require("react-native");
|
|
8202
|
+
var import_jsx_runtime51 = require("react/jsx-runtime");
|
|
7813
8203
|
function TypingIndicator({ style }) {
|
|
7814
8204
|
const theme = useTheme();
|
|
7815
8205
|
const dotColor = theme.colors.textSubtle;
|
|
7816
|
-
const anims =
|
|
7817
|
-
() => [new
|
|
8206
|
+
const anims = React39.useMemo(
|
|
8207
|
+
() => [new import_react_native50.Animated.Value(0.3), new import_react_native50.Animated.Value(0.3), new import_react_native50.Animated.Value(0.3)],
|
|
7818
8208
|
[]
|
|
7819
8209
|
);
|
|
7820
|
-
|
|
8210
|
+
React39.useEffect(() => {
|
|
7821
8211
|
const loops = [];
|
|
7822
8212
|
anims.forEach((a, idx) => {
|
|
7823
|
-
const seq =
|
|
7824
|
-
|
|
7825
|
-
|
|
8213
|
+
const seq = import_react_native50.Animated.sequence([
|
|
8214
|
+
import_react_native50.Animated.timing(a, { toValue: 1, duration: 420, useNativeDriver: true, delay: idx * 140 }),
|
|
8215
|
+
import_react_native50.Animated.timing(a, { toValue: 0.3, duration: 420, useNativeDriver: true })
|
|
7826
8216
|
]);
|
|
7827
|
-
const loop =
|
|
8217
|
+
const loop = import_react_native50.Animated.loop(seq);
|
|
7828
8218
|
loops.push(loop);
|
|
7829
8219
|
loop.start();
|
|
7830
8220
|
});
|
|
@@ -7832,8 +8222,8 @@ function TypingIndicator({ style }) {
|
|
|
7832
8222
|
loops.forEach((l) => l.stop());
|
|
7833
8223
|
};
|
|
7834
8224
|
}, [anims]);
|
|
7835
|
-
return /* @__PURE__ */ (0,
|
|
7836
|
-
|
|
8225
|
+
return /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(import_react_native50.View, { style: [{ flexDirection: "row", alignItems: "center" }, style], children: anims.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(
|
|
8226
|
+
import_react_native50.Animated.View,
|
|
7837
8227
|
{
|
|
7838
8228
|
style: {
|
|
7839
8229
|
width: 8,
|
|
@@ -7842,7 +8232,7 @@ function TypingIndicator({ style }) {
|
|
|
7842
8232
|
marginHorizontal: 3,
|
|
7843
8233
|
backgroundColor: dotColor,
|
|
7844
8234
|
opacity: a,
|
|
7845
|
-
transform: [{ translateY:
|
|
8235
|
+
transform: [{ translateY: import_react_native50.Animated.multiply(import_react_native50.Animated.subtract(a, 0.3), 2) }]
|
|
7846
8236
|
}
|
|
7847
8237
|
},
|
|
7848
8238
|
i
|
|
@@ -7850,36 +8240,37 @@ function TypingIndicator({ style }) {
|
|
|
7850
8240
|
}
|
|
7851
8241
|
|
|
7852
8242
|
// src/components/chat/ChatMessageList.tsx
|
|
7853
|
-
var
|
|
7854
|
-
var ChatMessageList =
|
|
8243
|
+
var import_jsx_runtime52 = require("react/jsx-runtime");
|
|
8244
|
+
var ChatMessageList = React40.forwardRef(
|
|
7855
8245
|
({
|
|
7856
8246
|
messages,
|
|
7857
8247
|
showTypingIndicator = false,
|
|
7858
8248
|
renderMessageContent,
|
|
7859
8249
|
onRetryMessage,
|
|
7860
8250
|
isRetryingMessage,
|
|
8251
|
+
onAttachmentLoadError,
|
|
7861
8252
|
contentStyle,
|
|
7862
8253
|
bottomInset = 0,
|
|
7863
8254
|
onNearBottomChange,
|
|
7864
8255
|
nearBottomThreshold = 200
|
|
7865
8256
|
}, ref) => {
|
|
7866
8257
|
const theme = useTheme();
|
|
7867
|
-
const listRef =
|
|
7868
|
-
const nearBottomRef =
|
|
7869
|
-
const initialScrollDoneRef =
|
|
7870
|
-
const lastMessageIdRef =
|
|
7871
|
-
const data =
|
|
8258
|
+
const listRef = React40.useRef(null);
|
|
8259
|
+
const nearBottomRef = React40.useRef(true);
|
|
8260
|
+
const initialScrollDoneRef = React40.useRef(false);
|
|
8261
|
+
const lastMessageIdRef = React40.useRef(null);
|
|
8262
|
+
const data = React40.useMemo(() => {
|
|
7872
8263
|
return [...messages].reverse();
|
|
7873
8264
|
}, [messages]);
|
|
7874
8265
|
const lastMessageId = messages.length > 0 ? messages[messages.length - 1].id : null;
|
|
7875
|
-
const keyExtractor =
|
|
7876
|
-
const scrollToBottom =
|
|
8266
|
+
const keyExtractor = React40.useCallback((m) => m.id, []);
|
|
8267
|
+
const scrollToBottom = React40.useCallback((options) => {
|
|
7877
8268
|
var _a;
|
|
7878
8269
|
const animated = (options == null ? void 0 : options.animated) ?? true;
|
|
7879
8270
|
(_a = listRef.current) == null ? void 0 : _a.scrollToOffset({ offset: 0, animated });
|
|
7880
8271
|
}, []);
|
|
7881
|
-
|
|
7882
|
-
const handleScroll =
|
|
8272
|
+
React40.useImperativeHandle(ref, () => ({ scrollToBottom }), [scrollToBottom]);
|
|
8273
|
+
const handleScroll = React40.useCallback(
|
|
7883
8274
|
(e) => {
|
|
7884
8275
|
const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;
|
|
7885
8276
|
const distanceFromBottom = Math.max(contentOffset.y - Math.max(bottomInset, 0), 0);
|
|
@@ -7891,7 +8282,7 @@ var ChatMessageList = React39.forwardRef(
|
|
|
7891
8282
|
},
|
|
7892
8283
|
[bottomInset, nearBottomThreshold, onNearBottomChange]
|
|
7893
8284
|
);
|
|
7894
|
-
|
|
8285
|
+
React40.useEffect(() => {
|
|
7895
8286
|
if (!initialScrollDoneRef.current) return;
|
|
7896
8287
|
const lastId = messages.length > 0 ? messages[messages.length - 1].id : null;
|
|
7897
8288
|
const prevLastId = lastMessageIdRef.current;
|
|
@@ -7901,14 +8292,14 @@ var ChatMessageList = React39.forwardRef(
|
|
|
7901
8292
|
const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
|
|
7902
8293
|
return () => cancelAnimationFrame(id);
|
|
7903
8294
|
}, [messages, scrollToBottom]);
|
|
7904
|
-
|
|
8295
|
+
React40.useEffect(() => {
|
|
7905
8296
|
if (showTypingIndicator && nearBottomRef.current) {
|
|
7906
8297
|
const id = requestAnimationFrame(() => scrollToBottom({ animated: true }));
|
|
7907
8298
|
return () => cancelAnimationFrame(id);
|
|
7908
8299
|
}
|
|
7909
8300
|
return void 0;
|
|
7910
8301
|
}, [showTypingIndicator, scrollToBottom]);
|
|
7911
|
-
const handleContentSizeChange =
|
|
8302
|
+
const handleContentSizeChange = React40.useCallback(() => {
|
|
7912
8303
|
if (initialScrollDoneRef.current) return;
|
|
7913
8304
|
initialScrollDoneRef.current = true;
|
|
7914
8305
|
lastMessageIdRef.current = messages.length > 0 ? messages[messages.length - 1].id : null;
|
|
@@ -7916,7 +8307,7 @@ var ChatMessageList = React39.forwardRef(
|
|
|
7916
8307
|
onNearBottomChange == null ? void 0 : onNearBottomChange(true);
|
|
7917
8308
|
requestAnimationFrame(() => scrollToBottom({ animated: false }));
|
|
7918
8309
|
}, [messages, onNearBottomChange, scrollToBottom]);
|
|
7919
|
-
const contentContainerStyle =
|
|
8310
|
+
const contentContainerStyle = React40.useMemo(
|
|
7920
8311
|
() => [
|
|
7921
8312
|
{
|
|
7922
8313
|
paddingHorizontal: theme.spacing.lg,
|
|
@@ -7926,28 +8317,29 @@ var ChatMessageList = React39.forwardRef(
|
|
|
7926
8317
|
],
|
|
7927
8318
|
[contentStyle, theme.spacing.lg, theme.spacing.sm]
|
|
7928
8319
|
);
|
|
7929
|
-
const renderSeparator =
|
|
7930
|
-
const listHeader =
|
|
7931
|
-
() => /* @__PURE__ */ (0,
|
|
7932
|
-
showTypingIndicator ? /* @__PURE__ */ (0,
|
|
7933
|
-
bottomInset > 0 ? /* @__PURE__ */ (0,
|
|
8320
|
+
const renderSeparator = React40.useCallback(() => /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_react_native51.View, { style: { height: theme.spacing.sm } }), [theme.spacing.sm]);
|
|
8321
|
+
const listHeader = React40.useMemo(
|
|
8322
|
+
() => /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(import_react_native51.View, { children: [
|
|
8323
|
+
showTypingIndicator ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_react_native51.View, { style: { marginTop: theme.spacing.sm, alignSelf: "flex-start", paddingHorizontal: theme.spacing.lg }, children: /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(TypingIndicator, {}) }) : null,
|
|
8324
|
+
bottomInset > 0 ? /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(import_react_native51.View, { style: { height: bottomInset } }) : null
|
|
7934
8325
|
] }),
|
|
7935
8326
|
[bottomInset, showTypingIndicator, theme.spacing.lg, theme.spacing.sm]
|
|
7936
8327
|
);
|
|
7937
|
-
const renderItem =
|
|
7938
|
-
({ item }) => /* @__PURE__ */ (0,
|
|
8328
|
+
const renderItem = React40.useCallback(
|
|
8329
|
+
({ item }) => /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
7939
8330
|
ChatMessageBubble,
|
|
7940
8331
|
{
|
|
7941
8332
|
message: item,
|
|
7942
8333
|
renderContent: renderMessageContent,
|
|
7943
8334
|
isLast: Boolean(lastMessageId && item.id === lastMessageId),
|
|
7944
8335
|
retrying: (isRetryingMessage == null ? void 0 : isRetryingMessage(item.id)) ?? false,
|
|
7945
|
-
onRetryMessage
|
|
8336
|
+
onRetryMessage,
|
|
8337
|
+
onAttachmentLoadError
|
|
7946
8338
|
}
|
|
7947
8339
|
),
|
|
7948
|
-
[isRetryingMessage, lastMessageId, onRetryMessage, renderMessageContent]
|
|
8340
|
+
[isRetryingMessage, lastMessageId, onAttachmentLoadError, onRetryMessage, renderMessageContent]
|
|
7949
8341
|
);
|
|
7950
|
-
return /* @__PURE__ */ (0,
|
|
8342
|
+
return /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(
|
|
7951
8343
|
import_bottom_sheet5.BottomSheetFlatList,
|
|
7952
8344
|
{
|
|
7953
8345
|
ref: listRef,
|
|
@@ -7970,7 +8362,7 @@ var ChatMessageList = React39.forwardRef(
|
|
|
7970
8362
|
ChatMessageList.displayName = "ChatMessageList";
|
|
7971
8363
|
|
|
7972
8364
|
// src/components/chat/ChatPage.tsx
|
|
7973
|
-
var
|
|
8365
|
+
var import_jsx_runtime53 = require("react/jsx-runtime");
|
|
7974
8366
|
function ChatPage({
|
|
7975
8367
|
header,
|
|
7976
8368
|
messages,
|
|
@@ -7978,6 +8370,7 @@ function ChatPage({
|
|
|
7978
8370
|
renderMessageContent,
|
|
7979
8371
|
onRetryMessage,
|
|
7980
8372
|
isRetryingMessage,
|
|
8373
|
+
onAttachmentLoadError,
|
|
7981
8374
|
topBanner,
|
|
7982
8375
|
composerTop,
|
|
7983
8376
|
composer,
|
|
@@ -7989,35 +8382,35 @@ function ChatPage({
|
|
|
7989
8382
|
}) {
|
|
7990
8383
|
const theme = useTheme();
|
|
7991
8384
|
const insets = (0, import_react_native_safe_area_context4.useSafeAreaInsets)();
|
|
7992
|
-
const [composerHeight, setComposerHeight] =
|
|
7993
|
-
const [composerTopHeight, setComposerTopHeight] =
|
|
7994
|
-
const footerBottomPadding =
|
|
8385
|
+
const [composerHeight, setComposerHeight] = React41.useState(0);
|
|
8386
|
+
const [composerTopHeight, setComposerTopHeight] = React41.useState(0);
|
|
8387
|
+
const footerBottomPadding = import_react_native52.Platform.OS === "ios" ? insets.bottom - 24 : insets.bottom + 10;
|
|
7995
8388
|
const totalComposerHeight = composerHeight + composerTopHeight;
|
|
7996
8389
|
const overlayBottom = totalComposerHeight + footerBottomPadding + theme.spacing.lg;
|
|
7997
8390
|
const bottomInset = totalComposerHeight + footerBottomPadding + theme.spacing.xl;
|
|
7998
|
-
const resolvedOverlay =
|
|
8391
|
+
const resolvedOverlay = React41.useMemo(() => {
|
|
7999
8392
|
var _a;
|
|
8000
8393
|
if (!overlay) return null;
|
|
8001
|
-
if (!
|
|
8394
|
+
if (!React41.isValidElement(overlay)) return overlay;
|
|
8002
8395
|
const prevStyle = (_a = overlay.props) == null ? void 0 : _a.style;
|
|
8003
|
-
return
|
|
8396
|
+
return React41.cloneElement(overlay, {
|
|
8004
8397
|
style: [prevStyle, { bottom: overlayBottom }]
|
|
8005
8398
|
});
|
|
8006
8399
|
}, [overlay, overlayBottom]);
|
|
8007
|
-
|
|
8400
|
+
React41.useEffect(() => {
|
|
8008
8401
|
if (composerTop) return;
|
|
8009
8402
|
setComposerTopHeight(0);
|
|
8010
8403
|
}, [composerTop]);
|
|
8011
|
-
return /* @__PURE__ */ (0,
|
|
8012
|
-
header ? /* @__PURE__ */ (0,
|
|
8013
|
-
topBanner ? /* @__PURE__ */ (0,
|
|
8014
|
-
/* @__PURE__ */ (0,
|
|
8015
|
-
/* @__PURE__ */ (0,
|
|
8016
|
-
|
|
8404
|
+
return /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_react_native52.View, { style: [{ flex: 1 }, style], children: [
|
|
8405
|
+
header ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native52.View, { children: header }) : null,
|
|
8406
|
+
topBanner ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(import_react_native52.View, { style: { paddingHorizontal: theme.spacing.lg, paddingTop: theme.spacing.sm }, children: topBanner }) : null,
|
|
8407
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(import_react_native52.View, { style: { flex: 1 }, children: [
|
|
8408
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
8409
|
+
import_react_native52.View,
|
|
8017
8410
|
{
|
|
8018
8411
|
style: { flex: 1 },
|
|
8019
8412
|
children: [
|
|
8020
|
-
/* @__PURE__ */ (0,
|
|
8413
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
8021
8414
|
ChatMessageList,
|
|
8022
8415
|
{
|
|
8023
8416
|
ref: listRef,
|
|
@@ -8026,6 +8419,7 @@ function ChatPage({
|
|
|
8026
8419
|
renderMessageContent,
|
|
8027
8420
|
onRetryMessage,
|
|
8028
8421
|
isRetryingMessage,
|
|
8422
|
+
onAttachmentLoadError,
|
|
8029
8423
|
onNearBottomChange,
|
|
8030
8424
|
bottomInset
|
|
8031
8425
|
}
|
|
@@ -8034,8 +8428,8 @@ function ChatPage({
|
|
|
8034
8428
|
]
|
|
8035
8429
|
}
|
|
8036
8430
|
),
|
|
8037
|
-
/* @__PURE__ */ (0,
|
|
8038
|
-
|
|
8431
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
|
|
8432
|
+
import_react_native52.View,
|
|
8039
8433
|
{
|
|
8040
8434
|
style: {
|
|
8041
8435
|
position: "absolute",
|
|
@@ -8047,15 +8441,15 @@ function ChatPage({
|
|
|
8047
8441
|
paddingBottom: footerBottomPadding
|
|
8048
8442
|
},
|
|
8049
8443
|
children: [
|
|
8050
|
-
composerTop ? /* @__PURE__ */ (0,
|
|
8051
|
-
|
|
8444
|
+
composerTop ? /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
8445
|
+
import_react_native52.View,
|
|
8052
8446
|
{
|
|
8053
8447
|
style: { marginBottom: theme.spacing.sm },
|
|
8054
8448
|
onLayout: (e) => setComposerTopHeight(e.nativeEvent.layout.height),
|
|
8055
8449
|
children: composerTop
|
|
8056
8450
|
}
|
|
8057
8451
|
) : null,
|
|
8058
|
-
/* @__PURE__ */ (0,
|
|
8452
|
+
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
|
|
8059
8453
|
ChatComposer,
|
|
8060
8454
|
{
|
|
8061
8455
|
...composer,
|
|
@@ -8071,15 +8465,15 @@ function ChatPage({
|
|
|
8071
8465
|
}
|
|
8072
8466
|
|
|
8073
8467
|
// src/components/chat/ScrollToBottomButton.tsx
|
|
8074
|
-
var
|
|
8075
|
-
var
|
|
8468
|
+
var React42 = __toESM(require("react"));
|
|
8469
|
+
var import_react_native53 = require("react-native");
|
|
8076
8470
|
var import_react_native_reanimated2 = __toESM(require("react-native-reanimated"));
|
|
8077
|
-
var
|
|
8471
|
+
var import_jsx_runtime54 = require("react/jsx-runtime");
|
|
8078
8472
|
function ScrollToBottomButton({ visible, onPress, children, style }) {
|
|
8079
8473
|
const theme = useTheme();
|
|
8080
8474
|
const progress = (0, import_react_native_reanimated2.useSharedValue)(visible ? 1 : 0);
|
|
8081
|
-
const [pressed, setPressed] =
|
|
8082
|
-
|
|
8475
|
+
const [pressed, setPressed] = React42.useState(false);
|
|
8476
|
+
React42.useEffect(() => {
|
|
8083
8477
|
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) });
|
|
8084
8478
|
}, [progress, visible]);
|
|
8085
8479
|
const animStyle = (0, import_react_native_reanimated2.useAnimatedStyle)(() => ({
|
|
@@ -8088,7 +8482,7 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
|
|
|
8088
8482
|
}));
|
|
8089
8483
|
const bg = theme.scheme === "dark" ? "rgba(39,39,42,0.9)" : "rgba(244,244,245,0.95)";
|
|
8090
8484
|
const border = theme.scheme === "dark" ? withAlpha("#FFFFFF", 0.12) : withAlpha("#000000", 0.08);
|
|
8091
|
-
return /* @__PURE__ */ (0,
|
|
8485
|
+
return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
8092
8486
|
import_react_native_reanimated2.default.View,
|
|
8093
8487
|
{
|
|
8094
8488
|
pointerEvents: visible ? "auto" : "none",
|
|
@@ -8102,8 +8496,8 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
|
|
|
8102
8496
|
style,
|
|
8103
8497
|
animStyle
|
|
8104
8498
|
],
|
|
8105
|
-
children: /* @__PURE__ */ (0,
|
|
8106
|
-
|
|
8499
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
8500
|
+
import_react_native53.View,
|
|
8107
8501
|
{
|
|
8108
8502
|
style: {
|
|
8109
8503
|
width: 44,
|
|
@@ -8121,8 +8515,8 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
|
|
|
8121
8515
|
elevation: 5,
|
|
8122
8516
|
opacity: pressed ? 0.85 : 1
|
|
8123
8517
|
},
|
|
8124
|
-
children: /* @__PURE__ */ (0,
|
|
8125
|
-
|
|
8518
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
|
|
8519
|
+
import_react_native53.Pressable,
|
|
8126
8520
|
{
|
|
8127
8521
|
onPress,
|
|
8128
8522
|
onPressIn: () => setPressed(true),
|
|
@@ -8139,16 +8533,16 @@ function ScrollToBottomButton({ visible, onPress, children, style }) {
|
|
|
8139
8533
|
}
|
|
8140
8534
|
|
|
8141
8535
|
// src/components/chat/ChatHeader.tsx
|
|
8142
|
-
var
|
|
8143
|
-
var
|
|
8536
|
+
var import_react_native54 = require("react-native");
|
|
8537
|
+
var import_jsx_runtime55 = require("react/jsx-runtime");
|
|
8144
8538
|
function ChatHeader({ left, right, center, style }) {
|
|
8145
|
-
const flattenedStyle =
|
|
8539
|
+
const flattenedStyle = import_react_native54.StyleSheet.flatten([
|
|
8146
8540
|
{
|
|
8147
8541
|
paddingTop: 0
|
|
8148
8542
|
},
|
|
8149
8543
|
style
|
|
8150
8544
|
]);
|
|
8151
|
-
return /* @__PURE__ */ (0,
|
|
8545
|
+
return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
|
|
8152
8546
|
StudioSheetHeader,
|
|
8153
8547
|
{
|
|
8154
8548
|
left,
|
|
@@ -8160,13 +8554,13 @@ function ChatHeader({ left, right, center, style }) {
|
|
|
8160
8554
|
}
|
|
8161
8555
|
|
|
8162
8556
|
// src/components/chat/ForkNoticeBanner.tsx
|
|
8163
|
-
var
|
|
8164
|
-
var
|
|
8557
|
+
var import_react_native55 = require("react-native");
|
|
8558
|
+
var import_jsx_runtime56 = require("react/jsx-runtime");
|
|
8165
8559
|
function ForkNoticeBanner({ isOwner = true, title, description, style }) {
|
|
8166
8560
|
const theme = useTheme();
|
|
8167
8561
|
const resolvedTitle = title ?? (isOwner ? "Remixed app" : "Remix app");
|
|
8168
8562
|
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.");
|
|
8169
|
-
return /* @__PURE__ */ (0,
|
|
8563
|
+
return /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
8170
8564
|
Card,
|
|
8171
8565
|
{
|
|
8172
8566
|
variant: "surfaceRaised",
|
|
@@ -8181,8 +8575,8 @@ function ForkNoticeBanner({ isOwner = true, title, description, style }) {
|
|
|
8181
8575
|
},
|
|
8182
8576
|
style
|
|
8183
8577
|
],
|
|
8184
|
-
children: /* @__PURE__ */ (0,
|
|
8185
|
-
/* @__PURE__ */ (0,
|
|
8578
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime56.jsxs)(import_react_native55.View, { style: { minWidth: 0 }, children: [
|
|
8579
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
8186
8580
|
Text,
|
|
8187
8581
|
{
|
|
8188
8582
|
style: {
|
|
@@ -8196,7 +8590,7 @@ function ForkNoticeBanner({ isOwner = true, title, description, style }) {
|
|
|
8196
8590
|
children: resolvedTitle
|
|
8197
8591
|
}
|
|
8198
8592
|
),
|
|
8199
|
-
/* @__PURE__ */ (0,
|
|
8593
|
+
/* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
|
|
8200
8594
|
Text,
|
|
8201
8595
|
{
|
|
8202
8596
|
style: {
|
|
@@ -8214,16 +8608,16 @@ function ForkNoticeBanner({ isOwner = true, title, description, style }) {
|
|
|
8214
8608
|
}
|
|
8215
8609
|
|
|
8216
8610
|
// src/components/chat/ChatQueue.tsx
|
|
8217
|
-
var
|
|
8218
|
-
var
|
|
8219
|
-
var
|
|
8611
|
+
var React43 = __toESM(require("react"));
|
|
8612
|
+
var import_react_native56 = require("react-native");
|
|
8613
|
+
var import_jsx_runtime57 = require("react/jsx-runtime");
|
|
8220
8614
|
function ChatQueue({ items, onRemove }) {
|
|
8221
8615
|
const theme = useTheme();
|
|
8222
|
-
const [expanded, setExpanded] =
|
|
8223
|
-
const [canExpand, setCanExpand] =
|
|
8224
|
-
const [collapsedText, setCollapsedText] =
|
|
8225
|
-
const [removing, setRemoving] =
|
|
8226
|
-
const buildCollapsedText =
|
|
8616
|
+
const [expanded, setExpanded] = React43.useState({});
|
|
8617
|
+
const [canExpand, setCanExpand] = React43.useState({});
|
|
8618
|
+
const [collapsedText, setCollapsedText] = React43.useState({});
|
|
8619
|
+
const [removing, setRemoving] = React43.useState({});
|
|
8620
|
+
const buildCollapsedText = React43.useCallback((lines) => {
|
|
8227
8621
|
var _a, _b;
|
|
8228
8622
|
const line1 = ((_a = lines[0]) == null ? void 0 : _a.text) ?? "";
|
|
8229
8623
|
const line2 = ((_b = lines[1]) == null ? void 0 : _b.text) ?? "";
|
|
@@ -8239,7 +8633,7 @@ function ChatQueue({ items, onRemove }) {
|
|
|
8239
8633
|
return `${line1}
|
|
8240
8634
|
${trimmedLine2}\u2026 `;
|
|
8241
8635
|
}, []);
|
|
8242
|
-
|
|
8636
|
+
React43.useEffect(() => {
|
|
8243
8637
|
if (items.length === 0) return;
|
|
8244
8638
|
const ids = new Set(items.map((item) => item.id));
|
|
8245
8639
|
setExpanded((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => ids.has(id))));
|
|
@@ -8248,8 +8642,8 @@ ${trimmedLine2}\u2026 `;
|
|
|
8248
8642
|
setRemoving((prev) => Object.fromEntries(Object.entries(prev).filter(([id]) => ids.has(id))));
|
|
8249
8643
|
}, [items]);
|
|
8250
8644
|
if (items.length === 0) return null;
|
|
8251
|
-
return /* @__PURE__ */ (0,
|
|
8252
|
-
|
|
8645
|
+
return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
|
|
8646
|
+
import_react_native56.View,
|
|
8253
8647
|
{
|
|
8254
8648
|
style: {
|
|
8255
8649
|
borderWidth: 1,
|
|
@@ -8260,16 +8654,16 @@ ${trimmedLine2}\u2026 `;
|
|
|
8260
8654
|
backgroundColor: "transparent"
|
|
8261
8655
|
},
|
|
8262
8656
|
children: [
|
|
8263
|
-
/* @__PURE__ */ (0,
|
|
8264
|
-
/* @__PURE__ */ (0,
|
|
8657
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(Text, { variant: "caption", style: { marginBottom: theme.spacing.sm }, children: "Queue" }),
|
|
8658
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_react_native56.View, { style: { gap: theme.spacing.sm }, children: items.map((item) => {
|
|
8265
8659
|
const isExpanded = Boolean(expanded[item.id]);
|
|
8266
8660
|
const showToggle = Boolean(canExpand[item.id]);
|
|
8267
8661
|
const prompt = item.prompt ?? "";
|
|
8268
8662
|
const moreLabel = "more";
|
|
8269
8663
|
const displayPrompt = !isExpanded && showToggle && collapsedText[item.id] ? collapsedText[item.id] : prompt;
|
|
8270
8664
|
const isRemoving = Boolean(removing[item.id]);
|
|
8271
|
-
return /* @__PURE__ */ (0,
|
|
8272
|
-
|
|
8665
|
+
return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
|
|
8666
|
+
import_react_native56.View,
|
|
8273
8667
|
{
|
|
8274
8668
|
style: {
|
|
8275
8669
|
flexDirection: "row",
|
|
@@ -8281,8 +8675,8 @@ ${trimmedLine2}\u2026 `;
|
|
|
8281
8675
|
backgroundColor: withAlpha(theme.colors.surface, theme.scheme === "dark" ? 0.8 : 0.9)
|
|
8282
8676
|
},
|
|
8283
8677
|
children: [
|
|
8284
|
-
/* @__PURE__ */ (0,
|
|
8285
|
-
!canExpand[item.id] ? /* @__PURE__ */ (0,
|
|
8678
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_react_native56.View, { style: { flex: 1 }, children: [
|
|
8679
|
+
!canExpand[item.id] ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
8286
8680
|
Text,
|
|
8287
8681
|
{
|
|
8288
8682
|
style: { position: "absolute", opacity: 0, zIndex: -1, width: "100%" },
|
|
@@ -8301,14 +8695,14 @@ ${trimmedLine2}\u2026 `;
|
|
|
8301
8695
|
children: prompt
|
|
8302
8696
|
}
|
|
8303
8697
|
) : null,
|
|
8304
|
-
/* @__PURE__ */ (0,
|
|
8698
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
|
|
8305
8699
|
Text,
|
|
8306
8700
|
{
|
|
8307
8701
|
variant: "bodyMuted",
|
|
8308
8702
|
numberOfLines: isExpanded ? void 0 : 2,
|
|
8309
8703
|
children: [
|
|
8310
8704
|
displayPrompt,
|
|
8311
|
-
!isExpanded && showToggle ? /* @__PURE__ */ (0,
|
|
8705
|
+
!isExpanded && showToggle ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
8312
8706
|
Text,
|
|
8313
8707
|
{
|
|
8314
8708
|
color: theme.colors.text,
|
|
@@ -8320,18 +8714,18 @@ ${trimmedLine2}\u2026 `;
|
|
|
8320
8714
|
]
|
|
8321
8715
|
}
|
|
8322
8716
|
),
|
|
8323
|
-
showToggle && isExpanded ? /* @__PURE__ */ (0,
|
|
8324
|
-
|
|
8717
|
+
showToggle && isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
8718
|
+
import_react_native56.Pressable,
|
|
8325
8719
|
{
|
|
8326
8720
|
onPress: () => setExpanded((prev) => ({ ...prev, [item.id]: false })),
|
|
8327
8721
|
hitSlop: 6,
|
|
8328
8722
|
style: { alignSelf: "flex-start", marginTop: 4 },
|
|
8329
|
-
children: /* @__PURE__ */ (0,
|
|
8723
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(Text, { variant: "captionMuted", color: theme.colors.text, children: "less" })
|
|
8330
8724
|
}
|
|
8331
8725
|
) : null
|
|
8332
8726
|
] }),
|
|
8333
|
-
/* @__PURE__ */ (0,
|
|
8334
|
-
|
|
8727
|
+
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
|
|
8728
|
+
import_react_native56.Pressable,
|
|
8335
8729
|
{
|
|
8336
8730
|
onPress: () => {
|
|
8337
8731
|
if (!onRemove || isRemoving) return;
|
|
@@ -8346,7 +8740,7 @@ ${trimmedLine2}\u2026 `;
|
|
|
8346
8740
|
},
|
|
8347
8741
|
hitSlop: 8,
|
|
8348
8742
|
style: { alignSelf: "center" },
|
|
8349
|
-
children: isRemoving ? /* @__PURE__ */ (0,
|
|
8743
|
+
children: isRemoving ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_react_native56.ActivityIndicator, { size: "small", color: theme.colors.text }) : /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(IconClose, { size: 14, colorToken: "text" })
|
|
8350
8744
|
}
|
|
8351
8745
|
)
|
|
8352
8746
|
]
|
|
@@ -8360,15 +8754,15 @@ ${trimmedLine2}\u2026 `;
|
|
|
8360
8754
|
}
|
|
8361
8755
|
|
|
8362
8756
|
// src/components/chat/AgentProgressCard.tsx
|
|
8363
|
-
var
|
|
8757
|
+
var import_react_native57 = require("react-native");
|
|
8364
8758
|
|
|
8365
8759
|
// src/components/icons/RemixXLoopLottie.tsx
|
|
8366
8760
|
var import_lottie_react_native = __toESM(require("lottie-react-native"));
|
|
8367
|
-
var
|
|
8761
|
+
var import_jsx_runtime58 = require("react/jsx-runtime");
|
|
8368
8762
|
var remixXLoopSource = require_remix_x_loop_lottie();
|
|
8369
8763
|
var Lottie = import_lottie_react_native.default;
|
|
8370
8764
|
function RemixXLoopLottie({ size = 24, style }) {
|
|
8371
|
-
return /* @__PURE__ */ (0,
|
|
8765
|
+
return /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
|
|
8372
8766
|
Lottie,
|
|
8373
8767
|
{
|
|
8374
8768
|
source: remixXLoopSource,
|
|
@@ -8380,7 +8774,7 @@ function RemixXLoopLottie({ size = 24, style }) {
|
|
|
8380
8774
|
}
|
|
8381
8775
|
|
|
8382
8776
|
// src/components/chat/AgentProgressCard.tsx
|
|
8383
|
-
var
|
|
8777
|
+
var import_jsx_runtime59 = require("react/jsx-runtime");
|
|
8384
8778
|
function titleForPhase(phase) {
|
|
8385
8779
|
if (phase === "planning") return "Planning";
|
|
8386
8780
|
if (phase === "reasoning") return "Reasoning";
|
|
@@ -8405,8 +8799,8 @@ function AgentProgressCard({ progress }) {
|
|
|
8405
8799
|
const showAnimatedStatusIcon = progress.status === "running";
|
|
8406
8800
|
const subtitle = progress.latestMessage || `Agent is ${phaseLabel.toLowerCase()}...`;
|
|
8407
8801
|
const todo = progress.todoSummary;
|
|
8408
|
-
return /* @__PURE__ */ (0,
|
|
8409
|
-
|
|
8802
|
+
return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(
|
|
8803
|
+
import_react_native57.View,
|
|
8410
8804
|
{
|
|
8411
8805
|
style: {
|
|
8412
8806
|
borderWidth: 1,
|
|
@@ -8417,23 +8811,23 @@ function AgentProgressCard({ progress }) {
|
|
|
8417
8811
|
backgroundColor: withAlpha(theme.colors.surface, theme.scheme === "dark" ? 0.84 : 0.94)
|
|
8418
8812
|
},
|
|
8419
8813
|
children: [
|
|
8420
|
-
/* @__PURE__ */ (0,
|
|
8421
|
-
/* @__PURE__ */ (0,
|
|
8422
|
-
/* @__PURE__ */ (0,
|
|
8423
|
-
/* @__PURE__ */ (0,
|
|
8424
|
-
showAnimatedStatusIcon ? /* @__PURE__ */ (0,
|
|
8814
|
+
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_react_native57.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginBottom: 4 }, children: [
|
|
8815
|
+
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Text, { variant: "caption", children: statusLabel }),
|
|
8816
|
+
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_react_native57.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
|
|
8817
|
+
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Text, { variant: "captionMuted", children: phaseLabel }),
|
|
8818
|
+
showAnimatedStatusIcon ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(RemixXLoopLottie, { size: 20, style: { marginLeft: 8 } }) : null
|
|
8425
8819
|
] })
|
|
8426
8820
|
] }),
|
|
8427
|
-
/* @__PURE__ */ (0,
|
|
8428
|
-
progress.changedFilesCount > 0 ? /* @__PURE__ */ (0,
|
|
8821
|
+
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Text, { variant: "bodyMuted", children: subtitle }),
|
|
8822
|
+
progress.changedFilesCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Text, { variant: "captionMuted", style: { marginTop: 8 }, children: [
|
|
8429
8823
|
"Updated files: ",
|
|
8430
8824
|
progress.changedFilesCount
|
|
8431
8825
|
] }) : null,
|
|
8432
|
-
progress.recentFiles.length > 0 ? /* @__PURE__ */ (0,
|
|
8826
|
+
progress.recentFiles.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_react_native57.View, { style: { marginTop: 6 }, children: progress.recentFiles.map((path) => /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Text, { variant: "captionMuted", numberOfLines: 1, children: [
|
|
8433
8827
|
"\u2022 ",
|
|
8434
8828
|
path
|
|
8435
8829
|
] }, path)) }) : null,
|
|
8436
|
-
todo ? /* @__PURE__ */ (0,
|
|
8830
|
+
todo ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Text, { variant: "captionMuted", style: { marginTop: 8 }, children: [
|
|
8437
8831
|
"Todos: ",
|
|
8438
8832
|
todo.completed,
|
|
8439
8833
|
"/",
|
|
@@ -8447,8 +8841,8 @@ function AgentProgressCard({ progress }) {
|
|
|
8447
8841
|
}
|
|
8448
8842
|
|
|
8449
8843
|
// src/components/chat/BundleProgressCard.tsx
|
|
8450
|
-
var
|
|
8451
|
-
var
|
|
8844
|
+
var import_react_native58 = require("react-native");
|
|
8845
|
+
var import_jsx_runtime60 = require("react/jsx-runtime");
|
|
8452
8846
|
function titleForStatus2(status) {
|
|
8453
8847
|
if (status === "succeeded") return "Completed";
|
|
8454
8848
|
if (status === "failed") return "Failed";
|
|
@@ -8460,8 +8854,8 @@ function BundleProgressCard({ progress }) {
|
|
|
8460
8854
|
const percent = Math.round(Math.max(0, Math.min(1, progress.progressValue)) * 100);
|
|
8461
8855
|
const fillColor = progress.status === "failed" ? theme.colors.danger : progress.status === "succeeded" ? theme.colors.success : theme.colors.warning;
|
|
8462
8856
|
const detail = progress.errorMessage || progress.phaseLabel;
|
|
8463
|
-
return /* @__PURE__ */ (0,
|
|
8464
|
-
|
|
8857
|
+
return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
|
|
8858
|
+
import_react_native58.View,
|
|
8465
8859
|
{
|
|
8466
8860
|
accessible: true,
|
|
8467
8861
|
accessibilityRole: "progressbar",
|
|
@@ -8476,15 +8870,15 @@ function BundleProgressCard({ progress }) {
|
|
|
8476
8870
|
backgroundColor: withAlpha(theme.colors.surface, theme.scheme === "dark" ? 0.84 : 0.94)
|
|
8477
8871
|
},
|
|
8478
8872
|
children: [
|
|
8479
|
-
/* @__PURE__ */ (0,
|
|
8480
|
-
/* @__PURE__ */ (0,
|
|
8481
|
-
/* @__PURE__ */ (0,
|
|
8873
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_react_native58.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }, children: [
|
|
8874
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Text, { variant: "caption", children: statusLabel }),
|
|
8875
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(Text, { variant: "captionMuted", children: [
|
|
8482
8876
|
percent,
|
|
8483
8877
|
"%"
|
|
8484
8878
|
] })
|
|
8485
8879
|
] }),
|
|
8486
|
-
/* @__PURE__ */ (0,
|
|
8487
|
-
|
|
8880
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
8881
|
+
import_react_native58.View,
|
|
8488
8882
|
{
|
|
8489
8883
|
style: {
|
|
8490
8884
|
width: "100%",
|
|
@@ -8493,8 +8887,8 @@ function BundleProgressCard({ progress }) {
|
|
|
8493
8887
|
backgroundColor: withAlpha(theme.colors.border, theme.scheme === "dark" ? 0.5 : 0.6),
|
|
8494
8888
|
overflow: "hidden"
|
|
8495
8889
|
},
|
|
8496
|
-
children: /* @__PURE__ */ (0,
|
|
8497
|
-
|
|
8890
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
|
|
8891
|
+
import_react_native58.View,
|
|
8498
8892
|
{
|
|
8499
8893
|
style: {
|
|
8500
8894
|
width: `${percent}%`,
|
|
@@ -8505,14 +8899,14 @@ function BundleProgressCard({ progress }) {
|
|
|
8505
8899
|
)
|
|
8506
8900
|
}
|
|
8507
8901
|
),
|
|
8508
|
-
/* @__PURE__ */ (0,
|
|
8902
|
+
/* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Text, { variant: "captionMuted", numberOfLines: 1, style: { marginTop: 8, minHeight: 16 }, children: detail })
|
|
8509
8903
|
]
|
|
8510
8904
|
}
|
|
8511
8905
|
);
|
|
8512
8906
|
}
|
|
8513
8907
|
|
|
8514
8908
|
// src/studio/ui/ChatPanel.tsx
|
|
8515
|
-
var
|
|
8909
|
+
var import_jsx_runtime61 = require("react/jsx-runtime");
|
|
8516
8910
|
function ChatPanel({
|
|
8517
8911
|
title = "Chat",
|
|
8518
8912
|
messages,
|
|
@@ -8532,14 +8926,15 @@ function ChatPanel({
|
|
|
8532
8926
|
onSend,
|
|
8533
8927
|
onRetryMessage,
|
|
8534
8928
|
isRetryingMessage,
|
|
8929
|
+
onAttachmentLoadError,
|
|
8535
8930
|
queueItems = [],
|
|
8536
8931
|
onRemoveQueueItem,
|
|
8537
8932
|
progress = null
|
|
8538
8933
|
}) {
|
|
8539
8934
|
const theme = useTheme();
|
|
8540
|
-
const listRef =
|
|
8541
|
-
const [nearBottom, setNearBottom] =
|
|
8542
|
-
const handleSend =
|
|
8935
|
+
const listRef = React44.useRef(null);
|
|
8936
|
+
const [nearBottom, setNearBottom] = React44.useState(true);
|
|
8937
|
+
const handleSend = React44.useCallback(
|
|
8543
8938
|
async (text, composerAttachments) => {
|
|
8544
8939
|
const all = composerAttachments ?? attachments;
|
|
8545
8940
|
await onSend(text, all.length > 0 ? all : void 0);
|
|
@@ -8553,25 +8948,25 @@ function ChatPanel({
|
|
|
8553
8948
|
},
|
|
8554
8949
|
[attachments, nearBottom, onClearAttachments, onSend]
|
|
8555
8950
|
);
|
|
8556
|
-
const handleScrollToBottom =
|
|
8951
|
+
const handleScrollToBottom = React44.useCallback(() => {
|
|
8557
8952
|
var _a;
|
|
8558
8953
|
(_a = listRef.current) == null ? void 0 : _a.scrollToBottom({ animated: true });
|
|
8559
8954
|
}, []);
|
|
8560
|
-
const header = /* @__PURE__ */ (0,
|
|
8955
|
+
const header = /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
8561
8956
|
ChatHeader,
|
|
8562
8957
|
{
|
|
8563
|
-
left: /* @__PURE__ */ (0,
|
|
8564
|
-
/* @__PURE__ */ (0,
|
|
8565
|
-
onNavigateHome ? /* @__PURE__ */ (0,
|
|
8958
|
+
left: /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(import_react_native59.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
|
|
8959
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(StudioSheetHeaderIconButton, { onPress: onBack, accessibilityLabel: "Back", style: { marginRight: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(IconBack, { size: 20, colorToken: "floatingContent" }) }),
|
|
8960
|
+
onNavigateHome ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(StudioSheetHeaderIconButton, { onPress: onNavigateHome, accessibilityLabel: "Home", children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(IconHome, { size: 20, colorToken: "floatingContent" }) }) : null
|
|
8566
8961
|
] }),
|
|
8567
|
-
right: /* @__PURE__ */ (0,
|
|
8568
|
-
onStartDraw ? /* @__PURE__ */ (0,
|
|
8569
|
-
/* @__PURE__ */ (0,
|
|
8962
|
+
right: /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(import_react_native59.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
|
|
8963
|
+
onStartDraw ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(StudioSheetHeaderIconButton, { onPress: onStartDraw, accessibilityLabel: "Draw", intent: "danger", style: { marginRight: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(IconDraw, { size: 20, colorToken: "onDanger" }) }) : null,
|
|
8964
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(IconClose, { size: 20, colorToken: "floatingContent" }) })
|
|
8570
8965
|
] }),
|
|
8571
8966
|
center: null
|
|
8572
8967
|
}
|
|
8573
8968
|
);
|
|
8574
|
-
const topBanner = shouldForkOnEdit ? /* @__PURE__ */ (0,
|
|
8969
|
+
const topBanner = shouldForkOnEdit ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
8575
8970
|
ForkNoticeBanner,
|
|
8576
8971
|
{
|
|
8577
8972
|
isOwner: !shouldForkOnEdit,
|
|
@@ -8580,22 +8975,22 @@ function ChatPanel({
|
|
|
8580
8975
|
) : null;
|
|
8581
8976
|
const showMessagesLoading = Boolean(loading) && messages.length === 0;
|
|
8582
8977
|
if (showMessagesLoading) {
|
|
8583
|
-
return /* @__PURE__ */ (0,
|
|
8584
|
-
/* @__PURE__ */ (0,
|
|
8585
|
-
topBanner ? /* @__PURE__ */ (0,
|
|
8586
|
-
/* @__PURE__ */ (0,
|
|
8587
|
-
/* @__PURE__ */ (0,
|
|
8588
|
-
/* @__PURE__ */ (0,
|
|
8589
|
-
/* @__PURE__ */ (0,
|
|
8978
|
+
return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(import_react_native59.View, { style: { flex: 1 }, children: [
|
|
8979
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(import_react_native59.View, { children: header }),
|
|
8980
|
+
topBanner ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(import_react_native59.View, { style: { paddingHorizontal: 16, paddingTop: 8 }, children: topBanner }) : null,
|
|
8981
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(import_react_native59.View, { style: { flex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 24, paddingVertical: 12 }, children: [
|
|
8982
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(import_react_native59.ActivityIndicator, {}),
|
|
8983
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(import_react_native59.View, { style: { height: 12 } }),
|
|
8984
|
+
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Text, { variant: "bodyMuted", children: "Loading messages\u2026" })
|
|
8590
8985
|
] })
|
|
8591
8986
|
] });
|
|
8592
8987
|
}
|
|
8593
8988
|
const bundleProgress = (progress == null ? void 0 : progress.bundle) ?? null;
|
|
8594
|
-
const queueTop = progress || queueItems.length > 0 ? /* @__PURE__ */ (0,
|
|
8595
|
-
progress ? bundleProgress ? /* @__PURE__ */ (0,
|
|
8596
|
-
!progress && queueItems.length > 0 ? /* @__PURE__ */ (0,
|
|
8989
|
+
const queueTop = progress || queueItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(import_react_native59.View, { style: { gap: theme.spacing.sm }, children: [
|
|
8990
|
+
progress ? bundleProgress ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(BundleProgressCard, { progress: bundleProgress }) : /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(AgentProgressCard, { progress }) : null,
|
|
8991
|
+
!progress && queueItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(ChatQueue, { items: queueItems, onRemove: onRemoveQueueItem }) : null
|
|
8597
8992
|
] }) : null;
|
|
8598
|
-
return /* @__PURE__ */ (0,
|
|
8993
|
+
return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
8599
8994
|
ChatPage,
|
|
8600
8995
|
{
|
|
8601
8996
|
header,
|
|
@@ -8603,18 +8998,19 @@ function ChatPanel({
|
|
|
8603
8998
|
showTypingIndicator,
|
|
8604
8999
|
onRetryMessage,
|
|
8605
9000
|
isRetryingMessage,
|
|
9001
|
+
onAttachmentLoadError,
|
|
8606
9002
|
topBanner,
|
|
8607
9003
|
composerTop: queueTop,
|
|
8608
9004
|
composerHorizontalPadding: 0,
|
|
8609
9005
|
listRef,
|
|
8610
9006
|
onNearBottomChange: setNearBottom,
|
|
8611
|
-
overlay: /* @__PURE__ */ (0,
|
|
9007
|
+
overlay: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
|
|
8612
9008
|
ScrollToBottomButton,
|
|
8613
9009
|
{
|
|
8614
9010
|
visible: !nearBottom,
|
|
8615
9011
|
onPress: handleScrollToBottom,
|
|
8616
9012
|
style: { bottom: 80 },
|
|
8617
|
-
children: /* @__PURE__ */ (0,
|
|
9013
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(IconArrowDown, { size: 20, colorToken: "floatingContent" })
|
|
8618
9014
|
}
|
|
8619
9015
|
),
|
|
8620
9016
|
composer: {
|
|
@@ -8634,9 +9030,9 @@ function ChatPanel({
|
|
|
8634
9030
|
}
|
|
8635
9031
|
|
|
8636
9032
|
// src/components/dialogs/ConfirmMergeRequestDialog.tsx
|
|
8637
|
-
var
|
|
8638
|
-
var
|
|
8639
|
-
var
|
|
9033
|
+
var React45 = __toESM(require("react"));
|
|
9034
|
+
var import_react_native60 = require("react-native");
|
|
9035
|
+
var import_jsx_runtime62 = require("react/jsx-runtime");
|
|
8640
9036
|
function ConfirmMergeRequestDialog({
|
|
8641
9037
|
visible,
|
|
8642
9038
|
onOpenChange,
|
|
@@ -8647,14 +9043,14 @@ function ConfirmMergeRequestDialog({
|
|
|
8647
9043
|
onTestFirst
|
|
8648
9044
|
}) {
|
|
8649
9045
|
const theme = useTheme();
|
|
8650
|
-
const close =
|
|
9046
|
+
const close = React45.useCallback(() => onOpenChange(false), [onOpenChange]);
|
|
8651
9047
|
const canConfirm = Boolean(mergeRequest) && !approveDisabled;
|
|
8652
|
-
const handleConfirm =
|
|
9048
|
+
const handleConfirm = React45.useCallback(() => {
|
|
8653
9049
|
if (!mergeRequest) return;
|
|
8654
9050
|
onOpenChange(false);
|
|
8655
9051
|
void onConfirm();
|
|
8656
9052
|
}, [mergeRequest, onConfirm, onOpenChange]);
|
|
8657
|
-
const handleTestFirst =
|
|
9053
|
+
const handleTestFirst = React45.useCallback(() => {
|
|
8658
9054
|
if (!mergeRequest) return;
|
|
8659
9055
|
onOpenChange(false);
|
|
8660
9056
|
void onTestFirst(mergeRequest);
|
|
@@ -8666,7 +9062,7 @@ function ConfirmMergeRequestDialog({
|
|
|
8666
9062
|
justifyContent: "center",
|
|
8667
9063
|
alignSelf: "stretch"
|
|
8668
9064
|
};
|
|
8669
|
-
return /* @__PURE__ */ (0,
|
|
9065
|
+
return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(
|
|
8670
9066
|
Modal,
|
|
8671
9067
|
{
|
|
8672
9068
|
visible,
|
|
@@ -8677,7 +9073,7 @@ function ConfirmMergeRequestDialog({
|
|
|
8677
9073
|
backgroundColor: theme.colors.background
|
|
8678
9074
|
},
|
|
8679
9075
|
children: [
|
|
8680
|
-
/* @__PURE__ */ (0,
|
|
9076
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(import_react_native60.View, { children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
8681
9077
|
Text,
|
|
8682
9078
|
{
|
|
8683
9079
|
style: {
|
|
@@ -8689,9 +9085,9 @@ function ConfirmMergeRequestDialog({
|
|
|
8689
9085
|
children: "Are you sure you want to approve this merge request?"
|
|
8690
9086
|
}
|
|
8691
9087
|
) }),
|
|
8692
|
-
/* @__PURE__ */ (0,
|
|
8693
|
-
/* @__PURE__ */ (0,
|
|
8694
|
-
|
|
9088
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(import_react_native60.View, { style: { marginTop: 16 }, children: [
|
|
9089
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
9090
|
+
import_react_native60.View,
|
|
8695
9091
|
{
|
|
8696
9092
|
style: [
|
|
8697
9093
|
fullWidthButtonBase,
|
|
@@ -8700,22 +9096,22 @@ function ConfirmMergeRequestDialog({
|
|
|
8700
9096
|
opacity: canConfirm ? 1 : 0.5
|
|
8701
9097
|
}
|
|
8702
9098
|
],
|
|
8703
|
-
children: /* @__PURE__ */ (0,
|
|
8704
|
-
|
|
9099
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
9100
|
+
import_react_native60.Pressable,
|
|
8705
9101
|
{
|
|
8706
9102
|
accessibilityRole: "button",
|
|
8707
9103
|
accessibilityLabel: "Approve Merge",
|
|
8708
9104
|
disabled: !canConfirm,
|
|
8709
9105
|
onPress: handleConfirm,
|
|
8710
9106
|
style: [fullWidthButtonBase, { flex: 1 }],
|
|
8711
|
-
children: /* @__PURE__ */ (0,
|
|
9107
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Text, { style: { textAlign: "center", color: theme.colors.onPrimary }, children: "Approve Merge" })
|
|
8712
9108
|
}
|
|
8713
9109
|
)
|
|
8714
9110
|
}
|
|
8715
9111
|
),
|
|
8716
|
-
/* @__PURE__ */ (0,
|
|
8717
|
-
/* @__PURE__ */ (0,
|
|
8718
|
-
|
|
9112
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(import_react_native60.View, { style: { height: 8 } }),
|
|
9113
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
9114
|
+
import_react_native60.View,
|
|
8719
9115
|
{
|
|
8720
9116
|
style: [
|
|
8721
9117
|
fullWidthButtonBase,
|
|
@@ -8726,22 +9122,22 @@ function ConfirmMergeRequestDialog({
|
|
|
8726
9122
|
opacity: isBuilding || !mergeRequest ? 0.5 : 1
|
|
8727
9123
|
}
|
|
8728
9124
|
],
|
|
8729
|
-
children: /* @__PURE__ */ (0,
|
|
8730
|
-
|
|
9125
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
9126
|
+
import_react_native60.Pressable,
|
|
8731
9127
|
{
|
|
8732
9128
|
accessibilityRole: "button",
|
|
8733
9129
|
accessibilityLabel: isBuilding ? "Preparing\u2026" : "Test edits first",
|
|
8734
9130
|
disabled: isBuilding || !mergeRequest,
|
|
8735
9131
|
onPress: handleTestFirst,
|
|
8736
9132
|
style: [fullWidthButtonBase, { flex: 1 }],
|
|
8737
|
-
children: /* @__PURE__ */ (0,
|
|
9133
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Text, { style: { textAlign: "center", color: theme.colors.text }, children: isBuilding ? "Preparing\u2026" : "Test edits first" })
|
|
8738
9134
|
}
|
|
8739
9135
|
)
|
|
8740
9136
|
}
|
|
8741
9137
|
),
|
|
8742
|
-
/* @__PURE__ */ (0,
|
|
8743
|
-
/* @__PURE__ */ (0,
|
|
8744
|
-
|
|
9138
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(import_react_native60.View, { style: { height: 8 } }),
|
|
9139
|
+
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
9140
|
+
import_react_native60.View,
|
|
8745
9141
|
{
|
|
8746
9142
|
style: [
|
|
8747
9143
|
fullWidthButtonBase,
|
|
@@ -8751,14 +9147,14 @@ function ConfirmMergeRequestDialog({
|
|
|
8751
9147
|
borderColor: theme.colors.border
|
|
8752
9148
|
}
|
|
8753
9149
|
],
|
|
8754
|
-
children: /* @__PURE__ */ (0,
|
|
8755
|
-
|
|
9150
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
|
|
9151
|
+
import_react_native60.Pressable,
|
|
8756
9152
|
{
|
|
8757
9153
|
accessibilityRole: "button",
|
|
8758
9154
|
accessibilityLabel: "Cancel",
|
|
8759
9155
|
onPress: close,
|
|
8760
9156
|
style: [fullWidthButtonBase, { flex: 1 }],
|
|
8761
|
-
children: /* @__PURE__ */ (0,
|
|
9157
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Text, { style: { textAlign: "center", color: theme.colors.text }, children: "Cancel" })
|
|
8762
9158
|
}
|
|
8763
9159
|
)
|
|
8764
9160
|
}
|
|
@@ -8770,7 +9166,7 @@ function ConfirmMergeRequestDialog({
|
|
|
8770
9166
|
}
|
|
8771
9167
|
|
|
8772
9168
|
// src/studio/ui/ConfirmMergeFlow.tsx
|
|
8773
|
-
var
|
|
9169
|
+
var import_jsx_runtime63 = require("react/jsx-runtime");
|
|
8774
9170
|
function ConfirmMergeFlow({
|
|
8775
9171
|
visible,
|
|
8776
9172
|
onOpenChange,
|
|
@@ -8781,7 +9177,7 @@ function ConfirmMergeFlow({
|
|
|
8781
9177
|
onConfirm,
|
|
8782
9178
|
onTestFirst
|
|
8783
9179
|
}) {
|
|
8784
|
-
return /* @__PURE__ */ (0,
|
|
9180
|
+
return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
|
|
8785
9181
|
ConfirmMergeRequestDialog,
|
|
8786
9182
|
{
|
|
8787
9183
|
visible,
|
|
@@ -8803,7 +9199,8 @@ function ConfirmMergeFlow({
|
|
|
8803
9199
|
}
|
|
8804
9200
|
|
|
8805
9201
|
// src/studio/hooks/useOptimisticChatMessages.ts
|
|
8806
|
-
var
|
|
9202
|
+
var React46 = __toESM(require("react"));
|
|
9203
|
+
var import_react_native61 = require("react-native");
|
|
8807
9204
|
function makeOptimisticId() {
|
|
8808
9205
|
return `optimistic:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 10)}`;
|
|
8809
9206
|
}
|
|
@@ -8814,6 +9211,28 @@ function toEpochMs2(createdAt) {
|
|
|
8814
9211
|
const t = Date.parse(String(createdAt));
|
|
8815
9212
|
return Number.isFinite(t) ? t : 0;
|
|
8816
9213
|
}
|
|
9214
|
+
async function resolveAttachmentDimensions(uris) {
|
|
9215
|
+
return Promise.all(
|
|
9216
|
+
uris.map(
|
|
9217
|
+
async (uri) => {
|
|
9218
|
+
try {
|
|
9219
|
+
const { width, height } = await new Promise((resolve, reject) => {
|
|
9220
|
+
import_react_native61.Image.getSize(
|
|
9221
|
+
uri,
|
|
9222
|
+
(w, h) => resolve({ width: w, height: h }),
|
|
9223
|
+
(err) => reject(err)
|
|
9224
|
+
);
|
|
9225
|
+
});
|
|
9226
|
+
if (width > 0 && height > 0) {
|
|
9227
|
+
return { uri, width: Math.round(width), height: Math.round(height) };
|
|
9228
|
+
}
|
|
9229
|
+
} catch {
|
|
9230
|
+
}
|
|
9231
|
+
return { uri };
|
|
9232
|
+
}
|
|
9233
|
+
)
|
|
9234
|
+
);
|
|
9235
|
+
}
|
|
8817
9236
|
function isOptimisticResolvedByServer(chatMessages, o) {
|
|
8818
9237
|
if (o.failed) return false;
|
|
8819
9238
|
const normalize = (s) => s.trim();
|
|
@@ -8842,11 +9261,11 @@ function useOptimisticChatMessages({
|
|
|
8842
9261
|
chatMessages,
|
|
8843
9262
|
onSendChat
|
|
8844
9263
|
}) {
|
|
8845
|
-
const [optimisticChat, setOptimisticChat] =
|
|
8846
|
-
|
|
9264
|
+
const [optimisticChat, setOptimisticChat] = React46.useState([]);
|
|
9265
|
+
React46.useEffect(() => {
|
|
8847
9266
|
setOptimisticChat([]);
|
|
8848
9267
|
}, [threadId]);
|
|
8849
|
-
const messages =
|
|
9268
|
+
const messages = React46.useMemo(() => {
|
|
8850
9269
|
if (!optimisticChat || optimisticChat.length === 0) return chatMessages;
|
|
8851
9270
|
const unresolved = optimisticChat.filter((o) => !isOptimisticResolvedByServer(chatMessages, o));
|
|
8852
9271
|
if (unresolved.length === 0) return chatMessages;
|
|
@@ -8856,13 +9275,22 @@ function useOptimisticChatMessages({
|
|
|
8856
9275
|
content: o.content,
|
|
8857
9276
|
createdAt: o.createdAtIso,
|
|
8858
9277
|
kind: "optimistic",
|
|
9278
|
+
attachments: (o.attachments ?? []).map((attachment, index) => ({
|
|
9279
|
+
id: `${o.id}:attachment:${index}`,
|
|
9280
|
+
name: `attachment-${index + 1}.png`,
|
|
9281
|
+
mimeType: "image/png",
|
|
9282
|
+
size: 1,
|
|
9283
|
+
uri: attachment.uri,
|
|
9284
|
+
width: attachment.width,
|
|
9285
|
+
height: attachment.height
|
|
9286
|
+
})),
|
|
8859
9287
|
meta: o.failed ? { kind: "optimistic", event: "send.failed", status: "error" } : { kind: "optimistic", event: "send.pending", status: "info" }
|
|
8860
9288
|
}));
|
|
8861
9289
|
const merged = [...chatMessages, ...optimisticAsChat];
|
|
8862
9290
|
merged.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
|
|
8863
9291
|
return merged;
|
|
8864
9292
|
}, [chatMessages, optimisticChat]);
|
|
8865
|
-
|
|
9293
|
+
React46.useEffect(() => {
|
|
8866
9294
|
if (optimisticChat.length === 0) return;
|
|
8867
9295
|
setOptimisticChat((prev) => {
|
|
8868
9296
|
if (prev.length === 0) return prev;
|
|
@@ -8870,7 +9298,7 @@ function useOptimisticChatMessages({
|
|
|
8870
9298
|
return next.length === prev.length ? prev : next;
|
|
8871
9299
|
});
|
|
8872
9300
|
}, [chatMessages, optimisticChat.length]);
|
|
8873
|
-
const onSend =
|
|
9301
|
+
const onSend = React46.useCallback(
|
|
8874
9302
|
async (text, attachments) => {
|
|
8875
9303
|
if (disableOptimistic) {
|
|
8876
9304
|
await onSendChat(text, attachments);
|
|
@@ -8879,7 +9307,7 @@ function useOptimisticChatMessages({
|
|
|
8879
9307
|
const createdAtIso = (/* @__PURE__ */ new Date()).toISOString();
|
|
8880
9308
|
const baseServerLastId = chatMessages.length > 0 ? chatMessages[chatMessages.length - 1].id : null;
|
|
8881
9309
|
const id = makeOptimisticId();
|
|
8882
|
-
const normalizedAttachments = attachments && attachments.length > 0 ?
|
|
9310
|
+
const normalizedAttachments = attachments && attachments.length > 0 ? await resolveAttachmentDimensions(attachments) : void 0;
|
|
8883
9311
|
setOptimisticChat((prev) => [
|
|
8884
9312
|
...prev,
|
|
8885
9313
|
{
|
|
@@ -8898,8 +9326,9 @@ function useOptimisticChatMessages({
|
|
|
8898
9326
|
},
|
|
8899
9327
|
[chatMessages, disableOptimistic, onSendChat]
|
|
8900
9328
|
);
|
|
8901
|
-
const onRetry =
|
|
9329
|
+
const onRetry = React46.useCallback(
|
|
8902
9330
|
async (messageId) => {
|
|
9331
|
+
var _a;
|
|
8903
9332
|
if (disableOptimistic) return;
|
|
8904
9333
|
const target = optimisticChat.find((m) => m.id === messageId);
|
|
8905
9334
|
if (!target || target.retrying) return;
|
|
@@ -8910,7 +9339,10 @@ function useOptimisticChatMessages({
|
|
|
8910
9339
|
)
|
|
8911
9340
|
);
|
|
8912
9341
|
try {
|
|
8913
|
-
await onSendChat(
|
|
9342
|
+
await onSendChat(
|
|
9343
|
+
target.content,
|
|
9344
|
+
(_a = target.attachments) == null ? void 0 : _a.map((att) => att.uri)
|
|
9345
|
+
);
|
|
8914
9346
|
setOptimisticChat(
|
|
8915
9347
|
(prev) => prev.map((m) => m.id === messageId ? { ...m, retrying: false } : m)
|
|
8916
9348
|
);
|
|
@@ -8922,7 +9354,7 @@ function useOptimisticChatMessages({
|
|
|
8922
9354
|
},
|
|
8923
9355
|
[chatMessages, disableOptimistic, onSendChat, optimisticChat]
|
|
8924
9356
|
);
|
|
8925
|
-
const isRetrying =
|
|
9357
|
+
const isRetrying = React46.useCallback(
|
|
8926
9358
|
(messageId) => {
|
|
8927
9359
|
return optimisticChat.some((m) => m.id === messageId && m.retrying);
|
|
8928
9360
|
},
|
|
@@ -8936,24 +9368,24 @@ var import_studio_control = require("@comergehq/studio-control");
|
|
|
8936
9368
|
|
|
8937
9369
|
// src/components/icons/RemixUpIcon.tsx
|
|
8938
9370
|
var import_react_native_svg3 = __toESM(require("react-native-svg"));
|
|
8939
|
-
var
|
|
9371
|
+
var import_jsx_runtime64 = require("react/jsx-runtime");
|
|
8940
9372
|
function RemixUpIcon({ width = 24, height = 24, ...props }) {
|
|
8941
|
-
return /* @__PURE__ */ (0,
|
|
8942
|
-
/* @__PURE__ */ (0,
|
|
9373
|
+
return /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)(import_react_native_svg3.default, { viewBox: "0 0 70 49", width, height, fill: "none", ...props, children: [
|
|
9374
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
|
|
8943
9375
|
import_react_native_svg3.Path,
|
|
8944
9376
|
{
|
|
8945
9377
|
d: "M34.706 7.62939e-05L34.7656 2.28882e-05L21.44 13.2661L0 34.8401L13.266 48.1061L34.706 26.5321L21.44 13.2661L34.706 7.62939e-05Z",
|
|
8946
9378
|
fill: "#00CBC0"
|
|
8947
9379
|
}
|
|
8948
9380
|
),
|
|
8949
|
-
/* @__PURE__ */ (0,
|
|
9381
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
|
|
8950
9382
|
import_react_native_svg3.Path,
|
|
8951
9383
|
{
|
|
8952
9384
|
d: "M47.972 13.266L34.7656 2.28882e-05L34.706 7.62939e-05L47.972 13.266L34.706 26.5321L56.28 48.106L69.546 34.84L47.972 13.266Z",
|
|
8953
9385
|
fill: "#FF1820"
|
|
8954
9386
|
}
|
|
8955
9387
|
),
|
|
8956
|
-
/* @__PURE__ */ (0,
|
|
9388
|
+
/* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
|
|
8957
9389
|
import_react_native_svg3.Path,
|
|
8958
9390
|
{
|
|
8959
9391
|
d: "M34.7656 2.28882e-05L21.44 13.2661L34.706 26.5321L47.972 13.266L34.7656 2.28882e-05Z",
|
|
@@ -8964,7 +9396,7 @@ function RemixUpIcon({ width = 24, height = 24, ...props }) {
|
|
|
8964
9396
|
}
|
|
8965
9397
|
|
|
8966
9398
|
// src/studio/ui/StudioOverlay.tsx
|
|
8967
|
-
var
|
|
9399
|
+
var import_jsx_runtime65 = require("react/jsx-runtime");
|
|
8968
9400
|
function StudioOverlay({
|
|
8969
9401
|
captureTargetRef,
|
|
8970
9402
|
app,
|
|
@@ -8995,6 +9427,7 @@ function StudioOverlay({
|
|
|
8995
9427
|
chatSending,
|
|
8996
9428
|
chatShowTypingIndicator,
|
|
8997
9429
|
onSendChat,
|
|
9430
|
+
onChatAttachmentLoadError,
|
|
8998
9431
|
chatQueueItems,
|
|
8999
9432
|
onRemoveQueueItem,
|
|
9000
9433
|
chatProgress,
|
|
@@ -9008,15 +9441,15 @@ function StudioOverlay({
|
|
|
9008
9441
|
onSwitchRelatedApp
|
|
9009
9442
|
}) {
|
|
9010
9443
|
const theme = useTheme();
|
|
9011
|
-
const { width } = (0,
|
|
9012
|
-
const [sheetOpen, setSheetOpen] =
|
|
9013
|
-
const sheetOpenRef =
|
|
9014
|
-
const pendingNavigateHomeRef =
|
|
9015
|
-
const [activePage, setActivePage] =
|
|
9016
|
-
const [drawing, setDrawing] =
|
|
9017
|
-
const [chatAttachments, setChatAttachments] =
|
|
9018
|
-
const [commentsAppId, setCommentsAppId] =
|
|
9019
|
-
const [commentsCount, setCommentsCount] =
|
|
9444
|
+
const { width } = (0, import_react_native62.useWindowDimensions)();
|
|
9445
|
+
const [sheetOpen, setSheetOpen] = React47.useState(false);
|
|
9446
|
+
const sheetOpenRef = React47.useRef(sheetOpen);
|
|
9447
|
+
const pendingNavigateHomeRef = React47.useRef(false);
|
|
9448
|
+
const [activePage, setActivePage] = React47.useState("preview");
|
|
9449
|
+
const [drawing, setDrawing] = React47.useState(false);
|
|
9450
|
+
const [chatAttachments, setChatAttachments] = React47.useState([]);
|
|
9451
|
+
const [commentsAppId, setCommentsAppId] = React47.useState(null);
|
|
9452
|
+
const [commentsCount, setCommentsCount] = React47.useState(null);
|
|
9020
9453
|
const threadId = (app == null ? void 0 : app.threadId) ?? null;
|
|
9021
9454
|
const isForking = chatForking || (app == null ? void 0 : app.status) === "forking";
|
|
9022
9455
|
const isBubbleLoading = (app == null ? void 0 : app.status) === "editing" || isBaseBundleDownloading;
|
|
@@ -9029,26 +9462,26 @@ function StudioOverlay({
|
|
|
9029
9462
|
chatMessages,
|
|
9030
9463
|
onSendChat
|
|
9031
9464
|
});
|
|
9032
|
-
const [confirmMrId, setConfirmMrId] =
|
|
9033
|
-
const confirmMr =
|
|
9465
|
+
const [confirmMrId, setConfirmMrId] = React47.useState(null);
|
|
9466
|
+
const confirmMr = React47.useMemo(
|
|
9034
9467
|
() => confirmMrId ? incomingMergeRequests.find((m) => m.id === confirmMrId) ?? null : null,
|
|
9035
9468
|
[confirmMrId, incomingMergeRequests]
|
|
9036
9469
|
);
|
|
9037
|
-
const handleSheetOpenChange =
|
|
9470
|
+
const handleSheetOpenChange = React47.useCallback((open) => {
|
|
9038
9471
|
setSheetOpen(open);
|
|
9039
|
-
if (!open)
|
|
9472
|
+
if (!open) import_react_native62.Keyboard.dismiss();
|
|
9040
9473
|
}, []);
|
|
9041
|
-
const closeSheet =
|
|
9474
|
+
const closeSheet = React47.useCallback(() => {
|
|
9042
9475
|
handleSheetOpenChange(false);
|
|
9043
9476
|
}, [handleSheetOpenChange]);
|
|
9044
|
-
const openSheet =
|
|
9045
|
-
const goToChat =
|
|
9477
|
+
const openSheet = React47.useCallback(() => setSheetOpen(true), []);
|
|
9478
|
+
const goToChat = React47.useCallback(() => {
|
|
9046
9479
|
setActivePage("chat");
|
|
9047
9480
|
openSheet();
|
|
9048
9481
|
}, [openSheet]);
|
|
9049
|
-
const backToPreview =
|
|
9050
|
-
if (
|
|
9051
|
-
|
|
9482
|
+
const backToPreview = React47.useCallback(() => {
|
|
9483
|
+
if (import_react_native62.Platform.OS !== "ios") {
|
|
9484
|
+
import_react_native62.Keyboard.dismiss();
|
|
9052
9485
|
setActivePage("preview");
|
|
9053
9486
|
return;
|
|
9054
9487
|
}
|
|
@@ -9060,15 +9493,15 @@ function StudioOverlay({
|
|
|
9060
9493
|
clearTimeout(t);
|
|
9061
9494
|
setActivePage("preview");
|
|
9062
9495
|
};
|
|
9063
|
-
const sub =
|
|
9496
|
+
const sub = import_react_native62.Keyboard.addListener("keyboardDidHide", finalize);
|
|
9064
9497
|
const t = setTimeout(finalize, 350);
|
|
9065
|
-
|
|
9498
|
+
import_react_native62.Keyboard.dismiss();
|
|
9066
9499
|
}, []);
|
|
9067
|
-
const startDraw =
|
|
9500
|
+
const startDraw = React47.useCallback(() => {
|
|
9068
9501
|
setDrawing(true);
|
|
9069
9502
|
closeSheet();
|
|
9070
9503
|
}, [closeSheet]);
|
|
9071
|
-
const handleDrawCapture =
|
|
9504
|
+
const handleDrawCapture = React47.useCallback(
|
|
9072
9505
|
(dataUrl) => {
|
|
9073
9506
|
setChatAttachments((prev) => [...prev, dataUrl]);
|
|
9074
9507
|
setDrawing(false);
|
|
@@ -9077,7 +9510,7 @@ function StudioOverlay({
|
|
|
9077
9510
|
},
|
|
9078
9511
|
[openSheet]
|
|
9079
9512
|
);
|
|
9080
|
-
const toggleSheet =
|
|
9513
|
+
const toggleSheet = React47.useCallback(async () => {
|
|
9081
9514
|
if (!sheetOpen) {
|
|
9082
9515
|
const shouldExitTest = Boolean(testingMrId) || isTesting;
|
|
9083
9516
|
if (shouldExitTest) {
|
|
@@ -9089,7 +9522,7 @@ function StudioOverlay({
|
|
|
9089
9522
|
closeSheet();
|
|
9090
9523
|
}
|
|
9091
9524
|
}, [closeSheet, isTesting, onRestoreBase, sheetOpen, testingMrId]);
|
|
9092
|
-
const handleTestMr =
|
|
9525
|
+
const handleTestMr = React47.useCallback(
|
|
9093
9526
|
async (mr) => {
|
|
9094
9527
|
if (!onTestMr) return;
|
|
9095
9528
|
await onTestMr(mr);
|
|
@@ -9097,46 +9530,46 @@ function StudioOverlay({
|
|
|
9097
9530
|
},
|
|
9098
9531
|
[closeSheet, onTestMr]
|
|
9099
9532
|
);
|
|
9100
|
-
const handleNavigateHome =
|
|
9533
|
+
const handleNavigateHome = React47.useCallback(() => {
|
|
9101
9534
|
if (!onNavigateHome) return;
|
|
9102
|
-
if (
|
|
9535
|
+
if (import_react_native62.Platform.OS !== "android") {
|
|
9103
9536
|
onNavigateHome();
|
|
9104
9537
|
return;
|
|
9105
9538
|
}
|
|
9106
9539
|
if (!sheetOpenRef.current) {
|
|
9107
|
-
|
|
9540
|
+
import_react_native62.InteractionManager.runAfterInteractions(() => {
|
|
9108
9541
|
onNavigateHome();
|
|
9109
9542
|
});
|
|
9110
9543
|
return;
|
|
9111
9544
|
}
|
|
9112
9545
|
pendingNavigateHomeRef.current = true;
|
|
9113
|
-
|
|
9546
|
+
import_react_native62.Keyboard.dismiss();
|
|
9114
9547
|
setActivePage("preview");
|
|
9115
9548
|
closeSheet();
|
|
9116
9549
|
}, [closeSheet, onNavigateHome]);
|
|
9117
|
-
const handleSheetDismiss =
|
|
9118
|
-
if (
|
|
9550
|
+
const handleSheetDismiss = React47.useCallback(() => {
|
|
9551
|
+
if (import_react_native62.Platform.OS !== "android") return;
|
|
9119
9552
|
if (!pendingNavigateHomeRef.current) return;
|
|
9120
9553
|
pendingNavigateHomeRef.current = false;
|
|
9121
|
-
|
|
9554
|
+
import_react_native62.InteractionManager.runAfterInteractions(() => {
|
|
9122
9555
|
onNavigateHome == null ? void 0 : onNavigateHome();
|
|
9123
9556
|
});
|
|
9124
9557
|
}, [onNavigateHome]);
|
|
9125
|
-
|
|
9558
|
+
React47.useEffect(() => {
|
|
9126
9559
|
if (!sheetOpen) {
|
|
9127
9560
|
return;
|
|
9128
9561
|
}
|
|
9129
9562
|
pendingNavigateHomeRef.current = false;
|
|
9130
9563
|
}, [sheetOpen]);
|
|
9131
|
-
|
|
9564
|
+
React47.useEffect(() => {
|
|
9132
9565
|
return () => {
|
|
9133
9566
|
pendingNavigateHomeRef.current = false;
|
|
9134
9567
|
};
|
|
9135
9568
|
}, []);
|
|
9136
|
-
|
|
9569
|
+
React47.useEffect(() => {
|
|
9137
9570
|
sheetOpenRef.current = sheetOpen;
|
|
9138
9571
|
}, [sheetOpen]);
|
|
9139
|
-
|
|
9572
|
+
React47.useEffect(() => {
|
|
9140
9573
|
const poller = (0, import_studio_control.startStudioControlPolling)((action) => {
|
|
9141
9574
|
if (action === "show" && !sheetOpenRef.current) openSheet();
|
|
9142
9575
|
if (action === "hide" && sheetOpenRef.current) closeSheet();
|
|
@@ -9144,17 +9577,17 @@ function StudioOverlay({
|
|
|
9144
9577
|
}, studioControlOptions);
|
|
9145
9578
|
return () => poller.stop();
|
|
9146
9579
|
}, [closeSheet, openSheet, studioControlOptions, toggleSheet]);
|
|
9147
|
-
|
|
9580
|
+
React47.useEffect(() => {
|
|
9148
9581
|
void (0, import_studio_control.publishComergeStudioUIState)(sheetOpen, studioControlOptions);
|
|
9149
9582
|
}, [sheetOpen, studioControlOptions]);
|
|
9150
|
-
return /* @__PURE__ */ (0,
|
|
9151
|
-
/* @__PURE__ */ (0,
|
|
9152
|
-
/* @__PURE__ */ (0,
|
|
9583
|
+
return /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(import_jsx_runtime65.Fragment, { children: [
|
|
9584
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(EdgeGlowFrame, { visible: isTesting, role: "accent", thickness: 40, intensity: 1 }),
|
|
9585
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(StudioBottomSheet, { open: sheetOpen, onOpenChange: handleSheetOpenChange, onDismiss: handleSheetDismiss, children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
9153
9586
|
StudioSheetPager,
|
|
9154
9587
|
{
|
|
9155
9588
|
activePage,
|
|
9156
9589
|
width,
|
|
9157
|
-
preview: /* @__PURE__ */ (0,
|
|
9590
|
+
preview: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
9158
9591
|
PreviewPanel,
|
|
9159
9592
|
{
|
|
9160
9593
|
app,
|
|
@@ -9188,7 +9621,7 @@ function StudioOverlay({
|
|
|
9188
9621
|
onSwitchRelatedApp
|
|
9189
9622
|
}
|
|
9190
9623
|
),
|
|
9191
|
-
chat: /* @__PURE__ */ (0,
|
|
9624
|
+
chat: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
9192
9625
|
ChatPanel,
|
|
9193
9626
|
{
|
|
9194
9627
|
messages: optimistic.messages,
|
|
@@ -9208,6 +9641,7 @@ function StudioOverlay({
|
|
|
9208
9641
|
onSend: optimistic.onSend,
|
|
9209
9642
|
onRetryMessage: optimistic.onRetry,
|
|
9210
9643
|
isRetryingMessage: optimistic.isRetrying,
|
|
9644
|
+
onAttachmentLoadError: onChatAttachmentLoadError,
|
|
9211
9645
|
queueItems: queueItemsForChat,
|
|
9212
9646
|
onRemoveQueueItem,
|
|
9213
9647
|
progress: chatProgress
|
|
@@ -9215,7 +9649,7 @@ function StudioOverlay({
|
|
|
9215
9649
|
)
|
|
9216
9650
|
}
|
|
9217
9651
|
) }),
|
|
9218
|
-
showBubble && /* @__PURE__ */ (0,
|
|
9652
|
+
showBubble && /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
9219
9653
|
Bubble,
|
|
9220
9654
|
{
|
|
9221
9655
|
visible: !sheetOpen && !drawing,
|
|
@@ -9224,10 +9658,10 @@ function StudioOverlay({
|
|
|
9224
9658
|
onPress: toggleSheet,
|
|
9225
9659
|
isLoading: isBubbleLoading,
|
|
9226
9660
|
loadingBorderTone: isBaseBundleDownloading ? "warning" : "default",
|
|
9227
|
-
children: /* @__PURE__ */ (0,
|
|
9661
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(import_react_native62.View, { style: { width: 28, height: 28, alignItems: "center", justifyContent: "center" }, children: isBubbleLoading ? /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(RemixXLoopLottie, { size: 24 }) : /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(RemixUpIcon, { width: 24, height: 24 }) })
|
|
9228
9662
|
}
|
|
9229
9663
|
),
|
|
9230
|
-
/* @__PURE__ */ (0,
|
|
9664
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
9231
9665
|
DrawModeOverlay,
|
|
9232
9666
|
{
|
|
9233
9667
|
visible: drawing,
|
|
@@ -9236,7 +9670,7 @@ function StudioOverlay({
|
|
|
9236
9670
|
onCapture: handleDrawCapture
|
|
9237
9671
|
}
|
|
9238
9672
|
),
|
|
9239
|
-
/* @__PURE__ */ (0,
|
|
9673
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
9240
9674
|
ConfirmMergeFlow,
|
|
9241
9675
|
{
|
|
9242
9676
|
visible: Boolean(confirmMr),
|
|
@@ -9250,7 +9684,7 @@ function StudioOverlay({
|
|
|
9250
9684
|
onTestFirst: handleTestMr
|
|
9251
9685
|
}
|
|
9252
9686
|
),
|
|
9253
|
-
/* @__PURE__ */ (0,
|
|
9687
|
+
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
|
|
9254
9688
|
AppCommentsSheet,
|
|
9255
9689
|
{
|
|
9256
9690
|
appId: commentsAppId,
|
|
@@ -9263,7 +9697,7 @@ function StudioOverlay({
|
|
|
9263
9697
|
}
|
|
9264
9698
|
|
|
9265
9699
|
// src/studio/hooks/useEditQueue.ts
|
|
9266
|
-
var
|
|
9700
|
+
var React48 = __toESM(require("react"));
|
|
9267
9701
|
|
|
9268
9702
|
// src/data/apps/edit-queue/remote.ts
|
|
9269
9703
|
var EditQueueRemoteDataSourceImpl = class extends BaseRemote {
|
|
@@ -9372,17 +9806,17 @@ var editQueueRepository = new EditQueueRepositoryImpl(
|
|
|
9372
9806
|
|
|
9373
9807
|
// src/studio/hooks/useEditQueue.ts
|
|
9374
9808
|
function useEditQueue(appId) {
|
|
9375
|
-
const [items, setItems] =
|
|
9376
|
-
const [loading, setLoading] =
|
|
9377
|
-
const [error, setError] =
|
|
9378
|
-
const activeRequestIdRef =
|
|
9809
|
+
const [items, setItems] = React48.useState([]);
|
|
9810
|
+
const [loading, setLoading] = React48.useState(false);
|
|
9811
|
+
const [error, setError] = React48.useState(null);
|
|
9812
|
+
const activeRequestIdRef = React48.useRef(0);
|
|
9379
9813
|
const foregroundSignal = useForegroundSignal(Boolean(appId));
|
|
9380
|
-
const upsertSorted =
|
|
9814
|
+
const upsertSorted = React48.useCallback((prev, nextItem) => {
|
|
9381
9815
|
const next = prev.some((x) => x.id === nextItem.id) ? prev.map((x) => x.id === nextItem.id ? nextItem : x) : [...prev, nextItem];
|
|
9382
9816
|
next.sort((a, b) => String(a.createdAt).localeCompare(String(b.createdAt)));
|
|
9383
9817
|
return next;
|
|
9384
9818
|
}, []);
|
|
9385
|
-
const refetch =
|
|
9819
|
+
const refetch = React48.useCallback(async () => {
|
|
9386
9820
|
if (!appId) {
|
|
9387
9821
|
setItems([]);
|
|
9388
9822
|
return;
|
|
@@ -9402,10 +9836,10 @@ function useEditQueue(appId) {
|
|
|
9402
9836
|
if (activeRequestIdRef.current === requestId) setLoading(false);
|
|
9403
9837
|
}
|
|
9404
9838
|
}, [appId]);
|
|
9405
|
-
|
|
9839
|
+
React48.useEffect(() => {
|
|
9406
9840
|
void refetch();
|
|
9407
9841
|
}, [refetch]);
|
|
9408
|
-
|
|
9842
|
+
React48.useEffect(() => {
|
|
9409
9843
|
if (!appId) return;
|
|
9410
9844
|
const unsubscribe = editQueueRepository.subscribeEditQueue(appId, {
|
|
9411
9845
|
onInsert: (item) => setItems((prev) => upsertSorted(prev, item)),
|
|
@@ -9414,7 +9848,7 @@ function useEditQueue(appId) {
|
|
|
9414
9848
|
});
|
|
9415
9849
|
return unsubscribe;
|
|
9416
9850
|
}, [appId, upsertSorted, foregroundSignal]);
|
|
9417
|
-
|
|
9851
|
+
React48.useEffect(() => {
|
|
9418
9852
|
if (!appId) return;
|
|
9419
9853
|
if (foregroundSignal <= 0) return;
|
|
9420
9854
|
void refetch();
|
|
@@ -9423,16 +9857,16 @@ function useEditQueue(appId) {
|
|
|
9423
9857
|
}
|
|
9424
9858
|
|
|
9425
9859
|
// src/studio/hooks/useEditQueueActions.ts
|
|
9426
|
-
var
|
|
9860
|
+
var React49 = __toESM(require("react"));
|
|
9427
9861
|
function useEditQueueActions(appId) {
|
|
9428
|
-
const update =
|
|
9862
|
+
const update = React49.useCallback(
|
|
9429
9863
|
async (queueItemId, payload) => {
|
|
9430
9864
|
if (!appId) return;
|
|
9431
9865
|
await editQueueRepository.update(appId, queueItemId, payload);
|
|
9432
9866
|
},
|
|
9433
9867
|
[appId]
|
|
9434
9868
|
);
|
|
9435
|
-
const cancel =
|
|
9869
|
+
const cancel = React49.useCallback(
|
|
9436
9870
|
async (queueItemId) => {
|
|
9437
9871
|
if (!appId) return;
|
|
9438
9872
|
await editQueueRepository.cancel(appId, queueItemId);
|
|
@@ -9443,7 +9877,7 @@ function useEditQueueActions(appId) {
|
|
|
9443
9877
|
}
|
|
9444
9878
|
|
|
9445
9879
|
// src/studio/hooks/useAgentRunProgress.ts
|
|
9446
|
-
var
|
|
9880
|
+
var React50 = __toESM(require("react"));
|
|
9447
9881
|
|
|
9448
9882
|
// src/data/agent-progress/repository.ts
|
|
9449
9883
|
function mapRun(row) {
|
|
@@ -9709,23 +10143,23 @@ function deriveView(run, events, nowMs) {
|
|
|
9709
10143
|
function useAgentRunProgress(threadId, opts) {
|
|
9710
10144
|
var _a;
|
|
9711
10145
|
const enabled = Boolean((opts == null ? void 0 : opts.enabled) ?? true);
|
|
9712
|
-
const [run, setRun] =
|
|
9713
|
-
const [events, setEvents] =
|
|
9714
|
-
const [loading, setLoading] =
|
|
9715
|
-
const [error, setError] =
|
|
9716
|
-
const activeRequestIdRef =
|
|
9717
|
-
const lastSeqRef =
|
|
9718
|
-
const runRef =
|
|
10146
|
+
const [run, setRun] = React50.useState(null);
|
|
10147
|
+
const [events, setEvents] = React50.useState([]);
|
|
10148
|
+
const [loading, setLoading] = React50.useState(false);
|
|
10149
|
+
const [error, setError] = React50.useState(null);
|
|
10150
|
+
const activeRequestIdRef = React50.useRef(0);
|
|
10151
|
+
const lastSeqRef = React50.useRef(0);
|
|
10152
|
+
const runRef = React50.useRef(null);
|
|
9719
10153
|
const foregroundSignal = useForegroundSignal(Boolean(threadId) && enabled);
|
|
9720
|
-
const [bundleTick, setBundleTick] =
|
|
9721
|
-
|
|
10154
|
+
const [bundleTick, setBundleTick] = React50.useState(0);
|
|
10155
|
+
React50.useEffect(() => {
|
|
9722
10156
|
lastSeqRef.current = 0;
|
|
9723
10157
|
runRef.current = null;
|
|
9724
10158
|
}, [threadId]);
|
|
9725
|
-
|
|
10159
|
+
React50.useEffect(() => {
|
|
9726
10160
|
runRef.current = run;
|
|
9727
10161
|
}, [run]);
|
|
9728
|
-
const refetch =
|
|
10162
|
+
const refetch = React50.useCallback(async () => {
|
|
9729
10163
|
if (!threadId || !enabled) {
|
|
9730
10164
|
setRun(null);
|
|
9731
10165
|
setEvents([]);
|
|
@@ -9761,15 +10195,15 @@ function useAgentRunProgress(threadId, opts) {
|
|
|
9761
10195
|
if (activeRequestIdRef.current === requestId) setLoading(false);
|
|
9762
10196
|
}
|
|
9763
10197
|
}, [enabled, threadId]);
|
|
9764
|
-
|
|
10198
|
+
React50.useEffect(() => {
|
|
9765
10199
|
void refetch();
|
|
9766
10200
|
}, [refetch]);
|
|
9767
|
-
|
|
10201
|
+
React50.useEffect(() => {
|
|
9768
10202
|
if (!threadId || !enabled) return;
|
|
9769
10203
|
if (foregroundSignal <= 0) return;
|
|
9770
10204
|
void refetch();
|
|
9771
10205
|
}, [enabled, foregroundSignal, refetch, threadId]);
|
|
9772
|
-
|
|
10206
|
+
React50.useEffect(() => {
|
|
9773
10207
|
if (!threadId || !enabled) return;
|
|
9774
10208
|
const unsubRuns = agentProgressRepository.subscribeThreadRuns(threadId, {
|
|
9775
10209
|
onInsert: (nextRun) => {
|
|
@@ -9799,7 +10233,7 @@ function useAgentRunProgress(threadId, opts) {
|
|
|
9799
10233
|
});
|
|
9800
10234
|
return unsubRuns;
|
|
9801
10235
|
}, [enabled, threadId, foregroundSignal]);
|
|
9802
|
-
|
|
10236
|
+
React50.useEffect(() => {
|
|
9803
10237
|
if (!enabled || !(run == null ? void 0 : run.id)) return;
|
|
9804
10238
|
const runId = run.id;
|
|
9805
10239
|
const processIncoming = (incoming) => {
|
|
@@ -9831,8 +10265,8 @@ function useAgentRunProgress(threadId, opts) {
|
|
|
9831
10265
|
});
|
|
9832
10266
|
return unsubscribe;
|
|
9833
10267
|
}, [enabled, run == null ? void 0 : run.id, foregroundSignal]);
|
|
9834
|
-
const view =
|
|
9835
|
-
|
|
10268
|
+
const view = React50.useMemo(() => deriveView(run, events, Date.now()), [bundleTick, events, run]);
|
|
10269
|
+
React50.useEffect(() => {
|
|
9836
10270
|
var _a2;
|
|
9837
10271
|
if (!((_a2 = view.bundle) == null ? void 0 : _a2.active)) return;
|
|
9838
10272
|
const interval = setInterval(() => {
|
|
@@ -9845,13 +10279,13 @@ function useAgentRunProgress(threadId, opts) {
|
|
|
9845
10279
|
}
|
|
9846
10280
|
|
|
9847
10281
|
// src/studio/hooks/useRelatedApps.ts
|
|
9848
|
-
var
|
|
10282
|
+
var React51 = __toESM(require("react"));
|
|
9849
10283
|
function useRelatedApps(appId) {
|
|
9850
|
-
const [relatedApps, setRelatedApps] =
|
|
9851
|
-
const [loading, setLoading] =
|
|
9852
|
-
const [error, setError] =
|
|
9853
|
-
const requestSeqRef =
|
|
9854
|
-
const fetchRelatedApps =
|
|
10284
|
+
const [relatedApps, setRelatedApps] = React51.useState(null);
|
|
10285
|
+
const [loading, setLoading] = React51.useState(false);
|
|
10286
|
+
const [error, setError] = React51.useState(null);
|
|
10287
|
+
const requestSeqRef = React51.useRef(0);
|
|
10288
|
+
const fetchRelatedApps = React51.useCallback(async () => {
|
|
9855
10289
|
if (!appId) {
|
|
9856
10290
|
setRelatedApps(null);
|
|
9857
10291
|
setError(null);
|
|
@@ -9877,7 +10311,7 @@ function useRelatedApps(appId) {
|
|
|
9877
10311
|
}
|
|
9878
10312
|
}
|
|
9879
10313
|
}, [appId]);
|
|
9880
|
-
|
|
10314
|
+
React51.useEffect(() => {
|
|
9881
10315
|
void fetchRelatedApps();
|
|
9882
10316
|
}, [fetchRelatedApps]);
|
|
9883
10317
|
return {
|
|
@@ -9889,7 +10323,7 @@ function useRelatedApps(appId) {
|
|
|
9889
10323
|
}
|
|
9890
10324
|
|
|
9891
10325
|
// src/studio/ComergeStudio.tsx
|
|
9892
|
-
var
|
|
10326
|
+
var import_jsx_runtime66 = require("react/jsx-runtime");
|
|
9893
10327
|
function ComergeStudio({
|
|
9894
10328
|
appId,
|
|
9895
10329
|
clientKey: clientKey2,
|
|
@@ -9905,13 +10339,13 @@ function ComergeStudio({
|
|
|
9905
10339
|
embeddedBaseBundles,
|
|
9906
10340
|
onSystemEvent
|
|
9907
10341
|
}) {
|
|
9908
|
-
const [activeAppId, setActiveAppId] =
|
|
9909
|
-
const [runtimeAppId, setRuntimeAppId] =
|
|
9910
|
-
const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] =
|
|
9911
|
-
const didSyncFromHostRef =
|
|
9912
|
-
const lastNotifiedRef =
|
|
9913
|
-
const platform =
|
|
9914
|
-
const notifyActiveAppChanged =
|
|
10342
|
+
const [activeAppId, setActiveAppId] = React52.useState(appId);
|
|
10343
|
+
const [runtimeAppId, setRuntimeAppId] = React52.useState(appId);
|
|
10344
|
+
const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React52.useState(null);
|
|
10345
|
+
const didSyncFromHostRef = React52.useRef(false);
|
|
10346
|
+
const lastNotifiedRef = React52.useRef(null);
|
|
10347
|
+
const platform = React52.useMemo(() => import_react_native63.Platform.OS === "ios" ? "ios" : "android", []);
|
|
10348
|
+
const notifyActiveAppChanged = React52.useCallback(
|
|
9915
10349
|
(nextAppId, source) => {
|
|
9916
10350
|
if (!onActiveAppChanged) return;
|
|
9917
10351
|
const trimmedAppId = nextAppId.trim();
|
|
@@ -9924,28 +10358,28 @@ function ComergeStudio({
|
|
|
9924
10358
|
},
|
|
9925
10359
|
[appKey, onActiveAppChanged]
|
|
9926
10360
|
);
|
|
9927
|
-
const setActiveAppIdWithSource =
|
|
10361
|
+
const setActiveAppIdWithSource = React52.useCallback(
|
|
9928
10362
|
(nextAppId, source) => {
|
|
9929
10363
|
setActiveAppId(nextAppId);
|
|
9930
10364
|
notifyActiveAppChanged(nextAppId, source);
|
|
9931
10365
|
},
|
|
9932
10366
|
[notifyActiveAppChanged]
|
|
9933
10367
|
);
|
|
9934
|
-
|
|
10368
|
+
React52.useEffect(() => {
|
|
9935
10369
|
const source = didSyncFromHostRef.current ? "host_route_sync" : "initial";
|
|
9936
10370
|
didSyncFromHostRef.current = true;
|
|
9937
10371
|
setActiveAppIdWithSource(appId, source);
|
|
9938
10372
|
setRuntimeAppId(appId);
|
|
9939
10373
|
setPendingRuntimeTargetAppId(null);
|
|
9940
10374
|
}, [appId, setActiveAppIdWithSource]);
|
|
9941
|
-
const captureTargetRef =
|
|
9942
|
-
return /* @__PURE__ */ (0,
|
|
10375
|
+
const captureTargetRef = React52.useRef(null);
|
|
10376
|
+
return /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
9943
10377
|
StudioBootstrap,
|
|
9944
10378
|
{
|
|
9945
10379
|
clientKey: clientKey2,
|
|
9946
10380
|
analyticsEnabled,
|
|
9947
|
-
fallback: /* @__PURE__ */ (0,
|
|
9948
|
-
children: ({ userId }) => /* @__PURE__ */ (0,
|
|
10381
|
+
fallback: /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(import_react_native63.View, { style: { flex: 1 } }),
|
|
10382
|
+
children: ({ userId }) => /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(import_bottom_sheet6.BottomSheetModalProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(LiquidGlassResetProvider, { resetTriggers: [appId, activeAppId, runtimeAppId], children: /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
9949
10383
|
ComergeStudioInner,
|
|
9950
10384
|
{
|
|
9951
10385
|
userId,
|
|
@@ -9995,11 +10429,11 @@ function ComergeStudioInner({
|
|
|
9995
10429
|
const { app, loading: appLoading } = useApp(activeAppId);
|
|
9996
10430
|
const { app: runtimeAppFromHook } = useApp(runtimeAppId, { enabled: runtimeAppId !== activeAppId });
|
|
9997
10431
|
const runtimeApp = runtimeAppId === activeAppId ? app : runtimeAppFromHook;
|
|
9998
|
-
const sawEditingOnPendingTargetRef =
|
|
9999
|
-
|
|
10432
|
+
const sawEditingOnPendingTargetRef = React52.useRef(false);
|
|
10433
|
+
React52.useEffect(() => {
|
|
10000
10434
|
sawEditingOnPendingTargetRef.current = false;
|
|
10001
10435
|
}, [pendingRuntimeTargetAppId]);
|
|
10002
|
-
|
|
10436
|
+
React52.useEffect(() => {
|
|
10003
10437
|
if (!pendingRuntimeTargetAppId) return;
|
|
10004
10438
|
if (activeAppId !== pendingRuntimeTargetAppId) return;
|
|
10005
10439
|
if ((app == null ? void 0 : app.status) === "editing") {
|
|
@@ -10017,13 +10451,13 @@ function ComergeStudioInner({
|
|
|
10017
10451
|
canRequestLatest: (runtimeApp == null ? void 0 : runtimeApp.status) === "ready",
|
|
10018
10452
|
embeddedBaseBundles
|
|
10019
10453
|
});
|
|
10020
|
-
const sawEditingOnActiveAppRef =
|
|
10021
|
-
const [showPostEditPreparing, setShowPostEditPreparing] =
|
|
10022
|
-
|
|
10454
|
+
const sawEditingOnActiveAppRef = React52.useRef(false);
|
|
10455
|
+
const [showPostEditPreparing, setShowPostEditPreparing] = React52.useState(false);
|
|
10456
|
+
React52.useEffect(() => {
|
|
10023
10457
|
sawEditingOnActiveAppRef.current = false;
|
|
10024
10458
|
setShowPostEditPreparing(false);
|
|
10025
10459
|
}, [activeAppId]);
|
|
10026
|
-
|
|
10460
|
+
React52.useEffect(() => {
|
|
10027
10461
|
if (!(app == null ? void 0 : app.id)) return;
|
|
10028
10462
|
if (app.status === "editing") {
|
|
10029
10463
|
sawEditingOnActiveAppRef.current = true;
|
|
@@ -10035,7 +10469,7 @@ function ComergeStudioInner({
|
|
|
10035
10469
|
sawEditingOnActiveAppRef.current = false;
|
|
10036
10470
|
}
|
|
10037
10471
|
}, [app == null ? void 0 : app.id, app == null ? void 0 : app.status]);
|
|
10038
|
-
|
|
10472
|
+
React52.useEffect(() => {
|
|
10039
10473
|
if (!showPostEditPreparing) return;
|
|
10040
10474
|
const stillProcessingBaseBundle = bundle.loading && bundle.loadingMode === "base" && !bundle.isTesting;
|
|
10041
10475
|
if (!stillProcessingBaseBundle) {
|
|
@@ -10047,19 +10481,19 @@ function ComergeStudioInner({
|
|
|
10047
10481
|
const editQueue = useEditQueue(activeAppId);
|
|
10048
10482
|
const agentProgress = useAgentRunProgress(threadId, { enabled: enableAgentProgress });
|
|
10049
10483
|
const editQueueActions = useEditQueueActions(activeAppId);
|
|
10050
|
-
const [lastEditQueueInfo, setLastEditQueueInfo] =
|
|
10051
|
-
const lastEditQueueInfoRef =
|
|
10052
|
-
const [suppressQueueUntilResponse, setSuppressQueueUntilResponse] =
|
|
10484
|
+
const [lastEditQueueInfo, setLastEditQueueInfo] = React52.useState(null);
|
|
10485
|
+
const lastEditQueueInfoRef = React52.useRef(null);
|
|
10486
|
+
const [suppressQueueUntilResponse, setSuppressQueueUntilResponse] = React52.useState(false);
|
|
10053
10487
|
const mergeRequests = useMergeRequests({ appId: activeAppId });
|
|
10054
|
-
const hasOpenOutgoingMr =
|
|
10488
|
+
const hasOpenOutgoingMr = React52.useMemo(() => {
|
|
10055
10489
|
return mergeRequests.lists.outgoing.some((mr) => mr.status === "open");
|
|
10056
10490
|
}, [mergeRequests.lists.outgoing]);
|
|
10057
|
-
const incomingReviewMrs =
|
|
10491
|
+
const incomingReviewMrs = React52.useMemo(() => {
|
|
10058
10492
|
if (!userId) return mergeRequests.lists.incoming;
|
|
10059
10493
|
return mergeRequests.lists.incoming.filter((mr) => mr.createdBy !== userId);
|
|
10060
10494
|
}, [mergeRequests.lists.incoming, userId]);
|
|
10061
10495
|
const uploader = useAttachmentUpload();
|
|
10062
|
-
const updateLastEditQueueInfo =
|
|
10496
|
+
const updateLastEditQueueInfo = React52.useCallback(
|
|
10063
10497
|
(info) => {
|
|
10064
10498
|
lastEditQueueInfoRef.current = info;
|
|
10065
10499
|
setLastEditQueueInfo(info);
|
|
@@ -10101,13 +10535,13 @@ function ComergeStudioInner({
|
|
|
10101
10535
|
}
|
|
10102
10536
|
});
|
|
10103
10537
|
const chatSendDisabled = false;
|
|
10104
|
-
const [processingMrId, setProcessingMrId] =
|
|
10105
|
-
const [testingMrId, setTestingMrId] =
|
|
10106
|
-
const [syncingUpstream, setSyncingUpstream] =
|
|
10107
|
-
const [upstreamSyncStatus, setUpstreamSyncStatus] =
|
|
10538
|
+
const [processingMrId, setProcessingMrId] = React52.useState(null);
|
|
10539
|
+
const [testingMrId, setTestingMrId] = React52.useState(null);
|
|
10540
|
+
const [syncingUpstream, setSyncingUpstream] = React52.useState(false);
|
|
10541
|
+
const [upstreamSyncStatus, setUpstreamSyncStatus] = React52.useState(null);
|
|
10108
10542
|
const isMrTestBuildInProgress = bundle.loading && bundle.loadingMode === "test";
|
|
10109
10543
|
const isBaseBundleDownloading = bundle.loading && bundle.loadingMode === "base" && !bundle.isTesting;
|
|
10110
|
-
const runtimePreparingText =
|
|
10544
|
+
const runtimePreparingText = React52.useMemo(() => {
|
|
10111
10545
|
const status = app == null ? void 0 : app.status;
|
|
10112
10546
|
if (status === "ready" && bundle.bundleStatus === "pending") {
|
|
10113
10547
|
return "Bundling app\u2026 this may take a few minutes";
|
|
@@ -10127,7 +10561,7 @@ function ComergeStudioInner({
|
|
|
10127
10561
|
return "Preparing app\u2026";
|
|
10128
10562
|
}
|
|
10129
10563
|
}, [app == null ? void 0 : app.status, bundle.bundleStatus]);
|
|
10130
|
-
const chatShowTypingIndicator =
|
|
10564
|
+
const chatShowTypingIndicator = React52.useMemo(() => {
|
|
10131
10565
|
var _a2;
|
|
10132
10566
|
if (agentProgress.hasLiveProgress) return false;
|
|
10133
10567
|
if (!thread.raw || thread.raw.length === 0) return false;
|
|
@@ -10136,12 +10570,12 @@ function ComergeStudioInner({
|
|
|
10136
10570
|
return payloadType !== "outcome";
|
|
10137
10571
|
}, [agentProgress.hasLiveProgress, thread.raw]);
|
|
10138
10572
|
const showChatProgress = agentProgress.hasLiveProgress || Boolean((_a = agentProgress.view.bundle) == null ? void 0 : _a.active);
|
|
10139
|
-
|
|
10573
|
+
React52.useEffect(() => {
|
|
10140
10574
|
updateLastEditQueueInfo(null);
|
|
10141
10575
|
setSuppressQueueUntilResponse(false);
|
|
10142
10576
|
setUpstreamSyncStatus(null);
|
|
10143
10577
|
}, [activeAppId, updateLastEditQueueInfo]);
|
|
10144
|
-
const handleSyncUpstream =
|
|
10578
|
+
const handleSyncUpstream = React52.useCallback(async () => {
|
|
10145
10579
|
if (!(app == null ? void 0 : app.id)) {
|
|
10146
10580
|
throw new Error("Missing app");
|
|
10147
10581
|
}
|
|
@@ -10154,7 +10588,7 @@ function ComergeStudioInner({
|
|
|
10154
10588
|
setSyncingUpstream(false);
|
|
10155
10589
|
}
|
|
10156
10590
|
}, [activeAppId, app == null ? void 0 : app.id]);
|
|
10157
|
-
|
|
10591
|
+
React52.useEffect(() => {
|
|
10158
10592
|
if (!(lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId)) return;
|
|
10159
10593
|
const stillPresent = editQueue.items.some((item) => item.id === lastEditQueueInfo.queueItemId);
|
|
10160
10594
|
if (!stillPresent) {
|
|
@@ -10162,7 +10596,7 @@ function ComergeStudioInner({
|
|
|
10162
10596
|
setSuppressQueueUntilResponse(false);
|
|
10163
10597
|
}
|
|
10164
10598
|
}, [editQueue.items, lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId]);
|
|
10165
|
-
const chatQueueItems =
|
|
10599
|
+
const chatQueueItems = React52.useMemo(() => {
|
|
10166
10600
|
var _a2;
|
|
10167
10601
|
if (suppressQueueUntilResponse && editQueue.items.length <= 1) {
|
|
10168
10602
|
return [];
|
|
@@ -10176,8 +10610,8 @@ function ComergeStudioInner({
|
|
|
10176
10610
|
return editQueue.items;
|
|
10177
10611
|
}, [editQueue.items, lastEditQueueInfo, suppressQueueUntilResponse]);
|
|
10178
10612
|
const { relatedApps, loading: relatedAppsLoading } = useRelatedApps(activeAppId);
|
|
10179
|
-
const [switchingRelatedAppId, setSwitchingRelatedAppId] =
|
|
10180
|
-
const handleOpenRelatedApps =
|
|
10613
|
+
const [switchingRelatedAppId, setSwitchingRelatedAppId] = React52.useState(null);
|
|
10614
|
+
const handleOpenRelatedApps = React52.useCallback(() => {
|
|
10181
10615
|
var _a2;
|
|
10182
10616
|
if (!relatedApps) return;
|
|
10183
10617
|
const ids = /* @__PURE__ */ new Set();
|
|
@@ -10186,7 +10620,7 @@ function ComergeStudioInner({
|
|
|
10186
10620
|
for (const remix of relatedApps.remixes) ids.add(remix.id);
|
|
10187
10621
|
void trackRelatedAppsOpened({ appId: relatedApps.current.id, relatedCount: ids.size });
|
|
10188
10622
|
}, [relatedApps]);
|
|
10189
|
-
const handleSwitchRelatedApp =
|
|
10623
|
+
const handleSwitchRelatedApp = React52.useCallback(
|
|
10190
10624
|
async (targetAppId) => {
|
|
10191
10625
|
var _a2;
|
|
10192
10626
|
if (!targetAppId || targetAppId === activeAppId) return;
|
|
@@ -10247,8 +10681,8 @@ function ComergeStudioInner({
|
|
|
10247
10681
|
setRuntimeAppId
|
|
10248
10682
|
]
|
|
10249
10683
|
);
|
|
10250
|
-
return /* @__PURE__ */ (0,
|
|
10251
|
-
/* @__PURE__ */ (0,
|
|
10684
|
+
return /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(import_react_native63.View, { style: [{ flex: 1 }, style], children: /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(import_react_native63.View, { ref: captureTargetRef, style: { flex: 1 }, collapsable: false, children: [
|
|
10685
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
10252
10686
|
RuntimeRenderer,
|
|
10253
10687
|
{
|
|
10254
10688
|
appKey,
|
|
@@ -10272,7 +10706,7 @@ function ComergeStudioInner({
|
|
|
10272
10706
|
}
|
|
10273
10707
|
}
|
|
10274
10708
|
),
|
|
10275
|
-
/* @__PURE__ */ (0,
|
|
10709
|
+
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
|
|
10276
10710
|
StudioOverlay,
|
|
10277
10711
|
{
|
|
10278
10712
|
captureTargetRef,
|
|
@@ -10329,6 +10763,9 @@ function ComergeStudioInner({
|
|
|
10329
10763
|
chatSending: actions.sending,
|
|
10330
10764
|
chatShowTypingIndicator,
|
|
10331
10765
|
onSendChat: (text, attachments) => actions.sendEdit({ prompt: text, attachments }),
|
|
10766
|
+
onChatAttachmentLoadError: () => {
|
|
10767
|
+
thread.recoverAttachmentUrls();
|
|
10768
|
+
},
|
|
10332
10769
|
chatQueueItems,
|
|
10333
10770
|
onRemoveQueueItem: (id) => editQueueActions.cancel(id),
|
|
10334
10771
|
chatProgress: showChatProgress ? agentProgress.view : null,
|