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