@comergehq/studio 0.1.16 → 0.1.18
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 +176 -120
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +184 -127
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/icons/StudioIcons.tsx +2 -0
- package/src/studio/hooks/useStudioActions.ts +1 -1
- package/src/studio/hooks/useThreadMessages.ts +7 -4
- package/src/studio/ui/PreviewPanel.tsx +35 -2
- package/src/studio/ui/StudioOverlay.tsx +4 -2
- package/src/studio/ui/preview-panel/PreviewPanelHeader.tsx +22 -2
package/package.json
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
Send,
|
|
13
13
|
X,
|
|
14
14
|
Check,
|
|
15
|
+
Share2,
|
|
15
16
|
} from 'lucide-react-native';
|
|
16
17
|
|
|
17
18
|
import { useTheme } from '../../theme';
|
|
@@ -62,5 +63,6 @@ export const IconSend = makeIcon(Send);
|
|
|
62
63
|
export const IconPlay = makeIcon(Play);
|
|
63
64
|
export const IconArrowDown = makeIcon(ArrowDown);
|
|
64
65
|
export const IconApprove = makeIcon(Check);
|
|
66
|
+
export const IconShare = makeIcon(Share2);
|
|
65
67
|
|
|
66
68
|
|
|
@@ -58,7 +58,6 @@ export function useStudioActions({
|
|
|
58
58
|
setSending(true);
|
|
59
59
|
setError(null);
|
|
60
60
|
try {
|
|
61
|
-
onEditStart?.();
|
|
62
61
|
let targetApp = app;
|
|
63
62
|
|
|
64
63
|
if (shouldForkOnEdit) {
|
|
@@ -73,6 +72,7 @@ export function useStudioActions({
|
|
|
73
72
|
|
|
74
73
|
const threadId = targetApp.threadId;
|
|
75
74
|
if (!threadId) throw new Error('No thread available for this app.');
|
|
75
|
+
onEditStart?.();
|
|
76
76
|
|
|
77
77
|
let attachmentMetas: AttachmentMeta[] | undefined;
|
|
78
78
|
if (attachments && attachments.length > 0 && uploadAttachments) {
|
|
@@ -83,9 +83,8 @@ export function useThreadMessages(threadId: string): UseThreadMessagesResult {
|
|
|
83
83
|
const foregroundSignal = useForegroundSignal(Boolean(threadId));
|
|
84
84
|
|
|
85
85
|
const upsertSorted = React.useCallback((prev: Message[], m: Message) => {
|
|
86
|
-
const include = !isQueuedHiddenMessage(m);
|
|
87
86
|
const next = prev.filter((x) => x.id !== m.id);
|
|
88
|
-
|
|
87
|
+
next.push(m);
|
|
89
88
|
next.sort(compareMessages);
|
|
90
89
|
return next;
|
|
91
90
|
}, []);
|
|
@@ -101,7 +100,7 @@ export function useThreadMessages(threadId: string): UseThreadMessagesResult {
|
|
|
101
100
|
try {
|
|
102
101
|
const list = await messagesRepository.list(threadId);
|
|
103
102
|
if (activeRequestIdRef.current !== requestId) return;
|
|
104
|
-
setRaw([...list].
|
|
103
|
+
setRaw([...list].sort(compareMessages));
|
|
105
104
|
} catch (e) {
|
|
106
105
|
if (activeRequestIdRef.current !== requestId) return;
|
|
107
106
|
setError(e instanceof Error ? e : new Error(String(e)));
|
|
@@ -131,7 +130,11 @@ export function useThreadMessages(threadId: string): UseThreadMessagesResult {
|
|
|
131
130
|
void refetch();
|
|
132
131
|
}, [foregroundSignal, refetch, threadId]);
|
|
133
132
|
|
|
134
|
-
const messages = React.useMemo(() =>
|
|
133
|
+
const messages = React.useMemo(() => {
|
|
134
|
+
const visible = raw.filter((m) => !isQueuedHiddenMessage(m));
|
|
135
|
+
const resolved = visible.length > 0 ? visible : raw;
|
|
136
|
+
return resolved.map(mapMessageToChatMessage);
|
|
137
|
+
}, [raw]);
|
|
135
138
|
|
|
136
139
|
return { raw, messages, loading, error, refetch };
|
|
137
140
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import { ActivityIndicator, View } from 'react-native';
|
|
2
|
+
import { ActivityIndicator, Platform, Share, View } from 'react-native';
|
|
3
3
|
|
|
4
4
|
import type { App } from '../../data/apps/types';
|
|
5
5
|
import type { MergeRequest } from '../../data/merge-requests/types';
|
|
6
|
+
import { log } from '../../core/logger';
|
|
6
7
|
import { PreviewPage } from '../../components/preview/PreviewPage';
|
|
7
8
|
import { Text } from '../../components/primitives/Text';
|
|
8
9
|
import { PreviewPanelHeader } from './preview-panel/PreviewPanelHeader';
|
|
@@ -59,6 +60,29 @@ export function PreviewPanel({
|
|
|
59
60
|
onOpenComments,
|
|
60
61
|
commentCountOverride,
|
|
61
62
|
}: PreviewPanelProps) {
|
|
63
|
+
const handleShare = React.useCallback(async () => {
|
|
64
|
+
if (!app || !app.isPublic) return;
|
|
65
|
+
const shareUrl = `https://comerge.ai/app/${app.id}`;
|
|
66
|
+
const message = app.name ? `${app.name} on Comerge\n${shareUrl}` : `Check out this app on Comerge\n${shareUrl}`;
|
|
67
|
+
try {
|
|
68
|
+
const title = app.name ?? 'Comerge app';
|
|
69
|
+
const payload =
|
|
70
|
+
Platform.OS === 'ios'
|
|
71
|
+
? {
|
|
72
|
+
title,
|
|
73
|
+
message,
|
|
74
|
+
}
|
|
75
|
+
: {
|
|
76
|
+
title,
|
|
77
|
+
message,
|
|
78
|
+
url: shareUrl,
|
|
79
|
+
};
|
|
80
|
+
await Share.share(payload);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
log.warn('PreviewPanel share failed', error);
|
|
83
|
+
}
|
|
84
|
+
}, [app]);
|
|
85
|
+
|
|
62
86
|
const { imageUrl, imageLoaded, setImageLoaded, creator, insights, stats, showProcessing, canSubmitMergeRequest } = usePreviewPanelData({
|
|
63
87
|
app,
|
|
64
88
|
isOwner,
|
|
@@ -67,7 +91,16 @@ export function PreviewPanel({
|
|
|
67
91
|
commentCountOverride,
|
|
68
92
|
});
|
|
69
93
|
|
|
70
|
-
const header =
|
|
94
|
+
const header = (
|
|
95
|
+
<PreviewPanelHeader
|
|
96
|
+
isOwner={isOwner}
|
|
97
|
+
isPublic={Boolean(app?.isPublic)}
|
|
98
|
+
onClose={onClose}
|
|
99
|
+
onNavigateHome={onNavigateHome}
|
|
100
|
+
onGoToChat={onGoToChat}
|
|
101
|
+
onShare={handleShare}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
71
104
|
|
|
72
105
|
if (loading || !app) {
|
|
73
106
|
return (
|
|
@@ -114,7 +114,9 @@ export function StudioOverlay({
|
|
|
114
114
|
const [commentsCount, setCommentsCount] = React.useState<number | null>(null);
|
|
115
115
|
|
|
116
116
|
const threadId = app?.threadId ?? null;
|
|
117
|
-
const
|
|
117
|
+
const isForking = chatForking || app?.status === 'forking';
|
|
118
|
+
const queueItemsForChat = isForking ? [] : chatQueueItems;
|
|
119
|
+
const disableOptimistic = Boolean(queueItemsForChat && queueItemsForChat.length > 0) || app?.status === 'editing';
|
|
118
120
|
const optimistic = useOptimisticChatMessages({
|
|
119
121
|
threadId,
|
|
120
122
|
shouldForkOnEdit,
|
|
@@ -271,7 +273,7 @@ export function StudioOverlay({
|
|
|
271
273
|
onNavigateHome={onNavigateHome}
|
|
272
274
|
onStartDraw={startDraw}
|
|
273
275
|
onSend={optimistic.onSend}
|
|
274
|
-
queueItems={
|
|
276
|
+
queueItems={queueItemsForChat}
|
|
275
277
|
onRemoveQueueItem={onRemoveQueueItem}
|
|
276
278
|
/>
|
|
277
279
|
}
|
|
@@ -3,16 +3,25 @@ import { View } from 'react-native';
|
|
|
3
3
|
|
|
4
4
|
import { StudioSheetHeader } from '../../../components/studio-sheet/StudioSheetHeader';
|
|
5
5
|
import { StudioSheetHeaderIconButton } from '../../../components/studio-sheet/StudioSheetHeaderIconButton';
|
|
6
|
-
import { IconChat, IconClose, IconHome } from '../../../components/icons/StudioIcons';
|
|
6
|
+
import { IconChat, IconClose, IconHome, IconShare } from '../../../components/icons/StudioIcons';
|
|
7
7
|
|
|
8
8
|
export type PreviewPanelHeaderProps = {
|
|
9
9
|
isOwner: boolean;
|
|
10
|
+
isPublic: boolean;
|
|
10
11
|
onClose: () => void;
|
|
11
12
|
onNavigateHome?: () => void;
|
|
12
13
|
onGoToChat: () => void;
|
|
14
|
+
onShare?: () => void;
|
|
13
15
|
};
|
|
14
16
|
|
|
15
|
-
export function PreviewPanelHeader({
|
|
17
|
+
export function PreviewPanelHeader({
|
|
18
|
+
isOwner,
|
|
19
|
+
isPublic,
|
|
20
|
+
onClose,
|
|
21
|
+
onNavigateHome,
|
|
22
|
+
onGoToChat,
|
|
23
|
+
onShare,
|
|
24
|
+
}: PreviewPanelHeaderProps) {
|
|
16
25
|
return (
|
|
17
26
|
<StudioSheetHeader
|
|
18
27
|
left={
|
|
@@ -36,6 +45,17 @@ export function PreviewPanelHeader({ isOwner, onClose, onNavigateHome, onGoToCha
|
|
|
36
45
|
<IconChat size={20} colorToken="onPrimary" />
|
|
37
46
|
</StudioSheetHeaderIconButton>
|
|
38
47
|
) : null}
|
|
48
|
+
{isPublic && onShare ? (
|
|
49
|
+
<StudioSheetHeaderIconButton
|
|
50
|
+
onPress={onShare}
|
|
51
|
+
accessibilityLabel="Share"
|
|
52
|
+
intent="primary"
|
|
53
|
+
appearance="glass"
|
|
54
|
+
style={{ marginRight: 8 }}
|
|
55
|
+
>
|
|
56
|
+
<IconShare size={20} colorToken="onPrimary" />
|
|
57
|
+
</StudioSheetHeaderIconButton>
|
|
58
|
+
) : null}
|
|
39
59
|
<StudioSheetHeaderIconButton onPress={onClose} accessibilityLabel="Close" appearance="glass" intent="primary">
|
|
40
60
|
<IconClose size={20} colorToken="onPrimary" />
|
|
41
61
|
</StudioSheetHeaderIconButton>
|