@comergehq/studio 0.1.2 → 0.1.4

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.
Files changed (174) hide show
  1. package/dist/index.d.mts +2 -10
  2. package/dist/index.d.ts +2 -10
  3. package/dist/index.js +293 -264
  4. package/dist/index.js.map +1 -1
  5. package/dist/index.mjs +251 -222
  6. package/dist/index.mjs.map +1 -1
  7. package/package.json +8 -5
  8. package/src/components/chat/ChatComposer.tsx +277 -0
  9. package/src/components/chat/ChatHeader.tsx +31 -0
  10. package/src/components/chat/ChatMessageBubble.tsx +69 -0
  11. package/src/components/chat/ChatMessageList.tsx +137 -0
  12. package/src/components/chat/ChatPage.tsx +69 -0
  13. package/src/components/chat/ForkNoticeBanner.tsx +66 -0
  14. package/src/components/chat/MultilineTextInput.tsx +46 -0
  15. package/src/components/chat/ScrollToBottomButton.tsx +78 -0
  16. package/src/components/chat/TypingIndicator.tsx +54 -0
  17. package/src/components/chat/index.ts +28 -0
  18. package/src/components/comments/AppCommentsSheet.tsx +213 -0
  19. package/src/components/comments/CommentRow.tsx +63 -0
  20. package/src/components/comments/formatTimeAgo.ts +3 -0
  21. package/src/components/comments/index.ts +3 -0
  22. package/src/components/comments/useAppComments.ts +74 -0
  23. package/src/components/comments/useAppDetails.ts +35 -0
  24. package/src/components/comments/useIosKeyboardSnapFix.ts +24 -0
  25. package/src/components/dialogs/ConfirmMergeRequestDialog.tsx +156 -0
  26. package/src/components/dialogs/index.ts +4 -0
  27. package/src/components/draw/DrawColorPicker.tsx +77 -0
  28. package/src/components/draw/DrawModeOverlay.tsx +144 -0
  29. package/src/components/draw/DrawSurface.tsx +127 -0
  30. package/src/components/draw/DrawToolbar.tsx +253 -0
  31. package/src/components/draw/index.ts +15 -0
  32. package/src/components/draw/optionalHaptics.ts +15 -0
  33. package/src/components/draw/strokes.ts +21 -0
  34. package/src/components/draw/types.ts +9 -0
  35. package/src/components/floating-draggable-button/FloatingDraggableButton.tsx +323 -0
  36. package/src/components/floating-draggable-button/constants.ts +17 -0
  37. package/src/components/floating-draggable-button/index.ts +4 -0
  38. package/src/components/floating-draggable-button/types.ts +63 -0
  39. package/src/components/icons/MergeIcon.tsx +14 -0
  40. package/src/components/icons/StudioIcons.tsx +66 -0
  41. package/src/components/index.ts +17 -0
  42. package/src/components/merge-requests/MergeRequestStatusCard.tsx +179 -0
  43. package/src/components/merge-requests/ReviewMergeRequestActionButton.tsx +62 -0
  44. package/src/components/merge-requests/ReviewMergeRequestCard.tsx +192 -0
  45. package/src/components/merge-requests/ReviewMergeRequestCarousel.tsx +132 -0
  46. package/src/components/merge-requests/index.ts +7 -0
  47. package/src/components/merge-requests/mergeRequestStatusDisplay.ts +23 -0
  48. package/src/components/merge-requests/toIsoString.ts +9 -0
  49. package/src/components/merge-requests/useControlledExpansion.ts +16 -0
  50. package/src/components/models/index.ts +9 -0
  51. package/src/components/models/types.ts +43 -0
  52. package/src/components/overlays/EdgeGlowFrame.tsx +105 -0
  53. package/src/components/overlays/index.ts +4 -0
  54. package/src/components/preview/PreviewHeroCard.tsx +58 -0
  55. package/src/components/preview/PreviewImage.tsx +22 -0
  56. package/src/components/preview/PreviewMetaRow.tsx +70 -0
  57. package/src/components/preview/PreviewPage.tsx +36 -0
  58. package/src/components/preview/PreviewPlaceholder.tsx +72 -0
  59. package/src/components/preview/PreviewStatusBadge.tsx +63 -0
  60. package/src/components/preview/StatsBar.tsx +109 -0
  61. package/src/components/preview/index.ts +22 -0
  62. package/src/components/primitives/Avatar.tsx +68 -0
  63. package/src/components/primitives/Button.tsx +102 -0
  64. package/src/components/primitives/Card.tsx +30 -0
  65. package/src/components/primitives/Divider.tsx +17 -0
  66. package/src/components/primitives/Icon.tsx +40 -0
  67. package/src/components/primitives/MarkdownText.tsx +72 -0
  68. package/src/components/primitives/Modal.tsx +53 -0
  69. package/src/components/primitives/Surface.tsx +42 -0
  70. package/src/components/primitives/Text.tsx +83 -0
  71. package/src/components/primitives/index.ts +35 -0
  72. package/src/components/primitives/types.ts +30 -0
  73. package/src/components/studio-sheet/StudioBottomSheet.tsx +114 -0
  74. package/src/components/studio-sheet/StudioSheetBackground.tsx +63 -0
  75. package/src/components/studio-sheet/StudioSheetHeader.tsx +35 -0
  76. package/src/components/studio-sheet/StudioSheetHeaderIconButton.tsx +109 -0
  77. package/src/components/studio-sheet/StudioSheetPager.tsx +66 -0
  78. package/src/components/studio-sheet/index.ts +18 -0
  79. package/src/components/studio-sheet/types.ts +5 -0
  80. package/src/components/utils/color.ts +25 -0
  81. package/src/components/utils/formatTimeAgo.ts +19 -0
  82. package/src/core/logger.ts +42 -0
  83. package/src/core/services/http/baseUrl.ts +3 -0
  84. package/src/core/services/http/index.ts +128 -0
  85. package/src/core/services/http/public.ts +33 -0
  86. package/src/core/services/supabase/auth.ts +41 -0
  87. package/src/core/services/supabase/client.ts +43 -0
  88. package/src/core/services/supabase/index.ts +7 -0
  89. package/src/data/agent/remote.ts +30 -0
  90. package/src/data/agent/repository.ts +34 -0
  91. package/src/data/agent/types.ts +28 -0
  92. package/src/data/apps/bundles/remote.ts +47 -0
  93. package/src/data/apps/bundles/repository.ts +35 -0
  94. package/src/data/apps/bundles/types.ts +27 -0
  95. package/src/data/apps/images/remote.ts +61 -0
  96. package/src/data/apps/images/repository.ts +47 -0
  97. package/src/data/apps/remote.ts +97 -0
  98. package/src/data/apps/repository.ts +185 -0
  99. package/src/data/apps/types.ts +206 -0
  100. package/src/data/attachment/remote.ts +32 -0
  101. package/src/data/attachment/repository.ts +40 -0
  102. package/src/data/attachment/types.ts +42 -0
  103. package/src/data/base-remote.ts +3 -0
  104. package/src/data/base-repository.ts +11 -0
  105. package/src/data/comments/likes/remote.ts +87 -0
  106. package/src/data/comments/likes/repository.ts +61 -0
  107. package/src/data/comments/likes/types.ts +47 -0
  108. package/src/data/comments/remote.ts +71 -0
  109. package/src/data/comments/repository.ts +53 -0
  110. package/src/data/comments/types.ts +60 -0
  111. package/src/data/github/remote.ts +23 -0
  112. package/src/data/github/repository.ts +35 -0
  113. package/src/data/github/types.ts +23 -0
  114. package/src/data/home/remote.ts +24 -0
  115. package/src/data/home/repository.ts +28 -0
  116. package/src/data/home/types.ts +70 -0
  117. package/src/data/index.ts +3 -0
  118. package/src/data/likes/remote.ts +57 -0
  119. package/src/data/likes/repository.ts +47 -0
  120. package/src/data/likes/types.ts +46 -0
  121. package/src/data/me/remote.ts +28 -0
  122. package/src/data/me/repository.ts +30 -0
  123. package/src/data/me/types.ts +14 -0
  124. package/src/data/merge-requests/remote.ts +76 -0
  125. package/src/data/merge-requests/repository.ts +66 -0
  126. package/src/data/merge-requests/types.ts +33 -0
  127. package/src/data/messages/remote.ts +21 -0
  128. package/src/data/messages/repository.ts +104 -0
  129. package/src/data/messages/types.ts +20 -0
  130. package/src/data/public/studio-config/remote.ts +19 -0
  131. package/src/data/public/studio-config/repository.ts +23 -0
  132. package/src/data/public/studio-config/types.ts +6 -0
  133. package/src/data/ratings/remote.ts +76 -0
  134. package/src/data/ratings/repository.ts +63 -0
  135. package/src/data/ratings/types.ts +57 -0
  136. package/src/data/threads/remote.ts +40 -0
  137. package/src/data/threads/repository.ts +41 -0
  138. package/src/data/threads/types.ts +25 -0
  139. package/src/data/types.ts +8 -0
  140. package/src/data/users/remote.ts +31 -0
  141. package/src/data/users/repository.ts +45 -0
  142. package/src/data/users/types.ts +15 -0
  143. package/src/index.ts +6 -0
  144. package/src/studio/ComergeStudio.tsx +239 -0
  145. package/src/studio/bootstrap/StudioBootstrap.tsx +45 -0
  146. package/src/studio/bootstrap/useStudioBootstrap.ts +55 -0
  147. package/src/studio/hooks/useApp.ts +83 -0
  148. package/src/studio/hooks/useAppStats.ts +111 -0
  149. package/src/studio/hooks/useAttachmentUpload.ts +59 -0
  150. package/src/studio/hooks/useBundleManager.ts +389 -0
  151. package/src/studio/hooks/useMergeRequests.ts +173 -0
  152. package/src/studio/hooks/useStudioActions.ts +96 -0
  153. package/src/studio/hooks/useThreadMessages.ts +85 -0
  154. package/src/studio/lib/chat.ts +34 -0
  155. package/src/studio/ui/ChatPanel.tsx +154 -0
  156. package/src/studio/ui/ConfirmMergeFlow.tsx +55 -0
  157. package/src/studio/ui/PreviewPanel.tsx +131 -0
  158. package/src/studio/ui/RuntimeRenderer.tsx +40 -0
  159. package/src/studio/ui/StudioOverlay.tsx +257 -0
  160. package/src/studio/ui/preview-panel/PressableCardRow.tsx +49 -0
  161. package/src/studio/ui/preview-panel/PreviewCollaborateSection.tsx +174 -0
  162. package/src/studio/ui/preview-panel/PreviewCustomizeSection.tsx +160 -0
  163. package/src/studio/ui/preview-panel/PreviewHeroSection.tsx +56 -0
  164. package/src/studio/ui/preview-panel/PreviewMetaSection.tsx +67 -0
  165. package/src/studio/ui/preview-panel/PreviewPanelHeader.tsx +48 -0
  166. package/src/studio/ui/preview-panel/SectionTitle.tsx +31 -0
  167. package/src/studio/ui/preview-panel/usePreviewPanelData.ts +132 -0
  168. package/src/studio/ui/preview-panel/utils.ts +29 -0
  169. package/src/theme/index.ts +5 -0
  170. package/src/theme/tokens.ts +118 -0
  171. package/src/theme/types.ts +90 -0
  172. package/src/theme/useTheme.ts +11 -0
  173. package/dist/assets/images/merge.svg +0 -3
  174. package/dist/merge-72UG27QV.svg +0 -3
@@ -0,0 +1,76 @@
1
+ import { api } from '../../core/services/http';
2
+ import type { ServiceResponse } from '../types';
3
+ import { BaseRemote } from '../base-remote';
4
+ import type {
5
+ MergeRequest,
6
+ MergeRequestStatus,
7
+ MergeRequestsByStatus,
8
+ OpenMergeRequestRequest,
9
+ UpdateMergeRequestRequest,
10
+ } from './types';
11
+
12
+ export interface MergeRequestsRemoteDataSource {
13
+ list(params: { sourceAppId?: string; targetAppId?: string; status?: MergeRequestStatus }): Promise<ServiceResponse<MergeRequest[]>>;
14
+ listByStatuses(params: {
15
+ sourceAppId?: string;
16
+ targetAppId?: string;
17
+ statuses: MergeRequestStatus[];
18
+ }): Promise<ServiceResponse<MergeRequestsByStatus | MergeRequest[]>>;
19
+ open(payload: OpenMergeRequestRequest): Promise<ServiceResponse<MergeRequest>>;
20
+ getById(mrId: string): Promise<ServiceResponse<MergeRequest>>;
21
+ update(mrId: string, payload: UpdateMergeRequestRequest): Promise<ServiceResponse<MergeRequest>>;
22
+ }
23
+
24
+ class MergeRequestsRemoteDataSourceImpl extends BaseRemote implements MergeRequestsRemoteDataSource {
25
+ async list(params: { sourceAppId?: string; targetAppId?: string; status?: MergeRequestStatus }): Promise<ServiceResponse<MergeRequest[]>> {
26
+ const query: Record<string, string> = {};
27
+ if (params.sourceAppId) query.sourceAppId = params.sourceAppId;
28
+ if (params.targetAppId) query.targetAppId = params.targetAppId;
29
+ if (params.status) query.status = params.status;
30
+ const { data } = await api.get<ServiceResponse<MergeRequest[]>>('/v1/merge-requests', { params: query });
31
+ return data;
32
+ }
33
+
34
+ async listByStatuses(params: {
35
+ sourceAppId?: string;
36
+ targetAppId?: string;
37
+ statuses: MergeRequestStatus[];
38
+ }): Promise<ServiceResponse<MergeRequestsByStatus | MergeRequest[]>> {
39
+ const query: Record<string, string | string[]> = {};
40
+ if (params.sourceAppId) query.sourceAppId = params.sourceAppId;
41
+ if (params.targetAppId) query.targetAppId = params.targetAppId;
42
+ query.status = params.statuses;
43
+ const { data } = await api.get<ServiceResponse<MergeRequestsByStatus | MergeRequest[]>>(
44
+ '/v1/merge-requests',
45
+ {
46
+ params: query,
47
+ paramsSerializer: {
48
+ indexes: null,
49
+ },
50
+ }
51
+ );
52
+ return data;
53
+ }
54
+
55
+ async open(payload: OpenMergeRequestRequest): Promise<ServiceResponse<MergeRequest>> {
56
+ const { data } = await api.post<ServiceResponse<MergeRequest>>('/v1/merge-requests', payload);
57
+ return data;
58
+ }
59
+
60
+ async getById(mrId: string): Promise<ServiceResponse<MergeRequest>> {
61
+ const { data } = await api.get<ServiceResponse<MergeRequest>>(`/v1/merge-requests/${encodeURIComponent(mrId)}`);
62
+ return data;
63
+ }
64
+
65
+ async update(mrId: string, payload: UpdateMergeRequestRequest): Promise<ServiceResponse<MergeRequest>> {
66
+ const { data } = await api.patch<ServiceResponse<MergeRequest>>(
67
+ `/v1/merge-requests/${encodeURIComponent(mrId)}`,
68
+ payload
69
+ );
70
+ return data;
71
+ }
72
+ }
73
+
74
+ export const mergeRequestsRemoteDataSource: MergeRequestsRemoteDataSource = new MergeRequestsRemoteDataSourceImpl();
75
+
76
+
@@ -0,0 +1,66 @@
1
+ import type { MergeRequestsRemoteDataSource } from './remote';
2
+ import { mergeRequestsRemoteDataSource } from './remote';
3
+ import type {
4
+ MergeRequest,
5
+ MergeRequestStatus,
6
+ MergeRequestsByStatus,
7
+ OpenMergeRequestRequest,
8
+ UpdateMergeRequestRequest,
9
+ } from './types';
10
+ import { BaseRepository } from '../../data/base-repository';
11
+
12
+ export interface MergeRequestsRepository {
13
+ list(params: { sourceAppId?: string; targetAppId?: string; status?: MergeRequestStatus }): Promise<MergeRequest[]>;
14
+ listByStatuses(params: {
15
+ sourceAppId?: string;
16
+ targetAppId?: string;
17
+ statuses: MergeRequestStatus[];
18
+ }): Promise<MergeRequestsByStatus>;
19
+ open(payload: OpenMergeRequestRequest): Promise<MergeRequest>;
20
+ getById(mrId: string): Promise<MergeRequest>;
21
+ update(mrId: string, payload: UpdateMergeRequestRequest): Promise<MergeRequest>;
22
+ }
23
+
24
+ class MergeRequestsRepositoryImpl extends BaseRepository implements MergeRequestsRepository {
25
+ constructor(private readonly remote: MergeRequestsRemoteDataSource) {
26
+ super();
27
+ }
28
+
29
+ async list(params: { sourceAppId?: string; targetAppId?: string; status?: MergeRequestStatus }): Promise<MergeRequest[]> {
30
+ const res = await this.remote.list(params);
31
+ return this.unwrapOrThrow(res);
32
+ }
33
+
34
+ async listByStatuses(params: {
35
+ sourceAppId?: string;
36
+ targetAppId?: string;
37
+ statuses: MergeRequestStatus[];
38
+ }): Promise<MergeRequestsByStatus> {
39
+ if (!params.statuses || params.statuses.length === 0) return {};
40
+ const res = await this.remote.listByStatuses(params);
41
+ const payload = this.unwrapOrThrow(res);
42
+ if (Array.isArray(payload)) {
43
+ return { [params.statuses[0]]: payload };
44
+ }
45
+ return payload;
46
+ }
47
+
48
+ async open(payload: OpenMergeRequestRequest): Promise<MergeRequest> {
49
+ const res = await this.remote.open(payload);
50
+ return this.unwrapOrThrow(res);
51
+ }
52
+
53
+ async getById(mrId: string): Promise<MergeRequest> {
54
+ const res = await this.remote.getById(mrId);
55
+ return this.unwrapOrThrow(res);
56
+ }
57
+
58
+ async update(mrId: string, payload: UpdateMergeRequestRequest): Promise<MergeRequest> {
59
+ const res = await this.remote.update(mrId, payload);
60
+ return this.unwrapOrThrow(res);
61
+ }
62
+ }
63
+
64
+ export const mergeRequestsRepository: MergeRequestsRepository = new MergeRequestsRepositoryImpl(mergeRequestsRemoteDataSource);
65
+
66
+
@@ -0,0 +1,33 @@
1
+ export type MergeRequestStatus = 'open' | 'approved' | 'rejected' | 'merged' | 'closed';
2
+ export type MergeRequestsByStatus = Partial<Record<MergeRequestStatus, MergeRequest[]>>;
3
+
4
+ export type MergeRequest = {
5
+ id: string;
6
+ sourceAppId: string;
7
+ sourceCommitId: string;
8
+ sourceTipCommitId: string | null;
9
+ targetAppId: string;
10
+ targetCommitId: string | null;
11
+ status: MergeRequestStatus;
12
+ title: string | null;
13
+ description: string | null;
14
+ createdBy: string;
15
+ reviewedBy: string | null;
16
+ mergedBy: string | null;
17
+ createdAt: string;
18
+ updatedAt: string;
19
+ mergedAt: string | null;
20
+ closedAt: string | null;
21
+ };
22
+
23
+ export type OpenMergeRequestRequest = {
24
+ sourceAppId: string;
25
+ };
26
+
27
+ export type UpdateMergeRequestRequest = {
28
+ title?: string;
29
+ description?: string;
30
+ status?: MergeRequestStatus;
31
+ };
32
+
33
+
@@ -0,0 +1,21 @@
1
+ import { api } from '../../core/services/http';
2
+ import type { ServiceResponse } from '../types';
3
+ import type { Message } from './types';
4
+ import { BaseRemote } from '../base-remote';
5
+
6
+ export interface MessagesRemoteDataSource {
7
+ list(threadId: string): Promise<ServiceResponse<Message[]>>;
8
+ }
9
+
10
+ class MessagesRemoteDataSourceImpl extends BaseRemote implements MessagesRemoteDataSource {
11
+ async list(threadId: string): Promise<ServiceResponse<Message[]>> {
12
+ const { data } = await api.get<ServiceResponse<Message[]>>(
13
+ `/v1/threads/${encodeURIComponent(threadId)}/messages`
14
+ );
15
+ return data;
16
+ }
17
+ }
18
+
19
+ export const messagesRemoteDataSource: MessagesRemoteDataSource = new MessagesRemoteDataSourceImpl();
20
+
21
+
@@ -0,0 +1,104 @@
1
+ import type { MessagesRemoteDataSource } from './remote';
2
+ import { messagesRemoteDataSource } from './remote';
3
+ import type { Message } from './types';
4
+ import { BaseRepository } from '../../data/base-repository';
5
+ import { getSupabaseClient } from '../../core/services/supabase';
6
+
7
+ type DbMessageRow = {
8
+ id: string;
9
+ app_id: string;
10
+ thread_id: string;
11
+ commit_id: string | null;
12
+ parent_message_id: string | null;
13
+ author_type: 'human' | 'ai' | 'system';
14
+ user_id: string | null;
15
+ payload: Record<string, unknown>;
16
+ reference_id: string | null;
17
+ created_at: string;
18
+ updated_at: string;
19
+ };
20
+
21
+ function mapDbRowToMessage(row: DbMessageRow): Message {
22
+ return {
23
+ id: row.id,
24
+ appId: row.app_id,
25
+ threadId: row.thread_id,
26
+ commitId: row.commit_id,
27
+ parentMessageId: row.parent_message_id,
28
+ authorType: row.author_type === 'system' ? 'ai' : row.author_type,
29
+ userId: row.user_id,
30
+ payload: row.payload,
31
+ referenceId: row.reference_id,
32
+ createdAt: row.created_at,
33
+ updatedAt: row.updated_at,
34
+ };
35
+ }
36
+
37
+ export interface MessagesRepository {
38
+ list(threadId: string): Promise<Message[]>;
39
+ subscribeThread(
40
+ threadId: string,
41
+ handlers: {
42
+ onInsert?: (m: Message) => void;
43
+ onUpdate?: (m: Message) => void;
44
+ onDelete?: (m: Message) => void;
45
+ }
46
+ ): () => void;
47
+ }
48
+
49
+ class MessagesRepositoryImpl extends BaseRepository implements MessagesRepository {
50
+ constructor(private readonly remote: MessagesRemoteDataSource) {
51
+ super();
52
+ }
53
+
54
+ async list(threadId: string): Promise<Message[]> {
55
+ const res = await this.remote.list(threadId);
56
+ return this.unwrapOrThrow(res);
57
+ }
58
+
59
+ subscribeThread(
60
+ threadId: string,
61
+ handlers: {
62
+ onInsert?: (m: Message) => void;
63
+ onUpdate?: (m: Message) => void;
64
+ onDelete?: (m: Message) => void;
65
+ }
66
+ ): () => void {
67
+ const supabase = getSupabaseClient();
68
+ const channel = supabase
69
+ .channel(`messages:thread:${threadId}`)
70
+ .on(
71
+ 'postgres_changes',
72
+ { event: 'INSERT', schema: 'public', table: 'message', filter: `thread_id=eq.${threadId}` },
73
+ (payload) => {
74
+ const row = payload.new as DbMessageRow;
75
+ handlers.onInsert?.(mapDbRowToMessage(row));
76
+ }
77
+ )
78
+ .on(
79
+ 'postgres_changes',
80
+ { event: 'UPDATE', schema: 'public', table: 'message', filter: `thread_id=eq.${threadId}` },
81
+ (payload) => {
82
+ const row = payload.new as DbMessageRow;
83
+ handlers.onUpdate?.(mapDbRowToMessage(row));
84
+ }
85
+ )
86
+ .on(
87
+ 'postgres_changes',
88
+ { event: 'DELETE', schema: 'public', table: 'message', filter: `thread_id=eq.${threadId}` },
89
+ (payload) => {
90
+ const row = payload.old as DbMessageRow;
91
+ handlers.onDelete?.(mapDbRowToMessage(row));
92
+ }
93
+ )
94
+ .subscribe();
95
+
96
+ return () => {
97
+ supabase.removeChannel(channel);
98
+ };
99
+ }
100
+ }
101
+
102
+ export const messagesRepository: MessagesRepository = new MessagesRepositoryImpl(messagesRemoteDataSource);
103
+
104
+
@@ -0,0 +1,20 @@
1
+ export type Message = {
2
+ id: string;
3
+ appId: string;
4
+ threadId: string;
5
+ commitId: string | null;
6
+ parentMessageId: string | null;
7
+ authorType: 'human' | 'ai';
8
+ userId: string | null;
9
+ payload: {
10
+ attachments?: AttachmentMeta[];
11
+ [key: string]: unknown;
12
+ };
13
+ referenceId: string | null;
14
+ createdAt: string;
15
+ updatedAt: string;
16
+ };
17
+
18
+ import type { AttachmentMeta } from '../../data/attachment/types';
19
+
20
+
@@ -0,0 +1,19 @@
1
+ import { publicApi } from "../../../core/services/http/public";
2
+ import type { ServiceResponse } from "../../types";
3
+ import { BaseRemote } from "../../base-remote";
4
+ import type { StudioConfig } from "./types";
5
+
6
+ export interface StudioConfigRemoteDataSource {
7
+ get(): Promise<ServiceResponse<StudioConfig>>;
8
+ }
9
+
10
+ class StudioConfigRemoteDataSourceImpl extends BaseRemote implements StudioConfigRemoteDataSource {
11
+ async get(): Promise<ServiceResponse<StudioConfig>> {
12
+ const { data } = await publicApi.get<ServiceResponse<StudioConfig>>("/v1/public/studio-config");
13
+ return data;
14
+ }
15
+ }
16
+
17
+ export const studioConfigRemoteDataSource: StudioConfigRemoteDataSource = new StudioConfigRemoteDataSourceImpl();
18
+
19
+
@@ -0,0 +1,23 @@
1
+ import { BaseRepository } from "../../base-repository";
2
+ import type { StudioConfig } from "./types";
3
+ import type { StudioConfigRemoteDataSource } from "./remote";
4
+ import { studioConfigRemoteDataSource } from "./remote";
5
+
6
+ export interface StudioConfigRepository {
7
+ get(): Promise<StudioConfig>;
8
+ }
9
+
10
+ class StudioConfigRepositoryImpl extends BaseRepository implements StudioConfigRepository {
11
+ constructor(private readonly remote: StudioConfigRemoteDataSource) {
12
+ super();
13
+ }
14
+
15
+ async get(): Promise<StudioConfig> {
16
+ const res = await this.remote.get();
17
+ return this.unwrapOrThrow(res);
18
+ }
19
+ }
20
+
21
+ export const studioConfigRepository: StudioConfigRepository = new StudioConfigRepositoryImpl(studioConfigRemoteDataSource);
22
+
23
+
@@ -0,0 +1,6 @@
1
+ export type StudioConfig = {
2
+ url: string;
3
+ anonKey: string;
4
+ };
5
+
6
+
@@ -0,0 +1,76 @@
1
+ import { api } from '../../core/services/http';
2
+ import type { ServiceResponse } from '../types';
3
+ import { BaseRemote } from '../base-remote';
4
+ import type {
5
+ AppRating,
6
+ AppRatingList,
7
+ AppRatingMutationResult,
8
+ AppRatingStatsResult,
9
+ CreateAppRatingInput,
10
+ ListAppRatingsQuery,
11
+ UpdateAppRatingInput,
12
+ } from './types';
13
+
14
+ export interface AppRatingsRemoteDataSource {
15
+ list(appId: string, query?: ListAppRatingsQuery): Promise<ServiceResponse<AppRatingList>>;
16
+ getById(appId: string, ratingId: string): Promise<ServiceResponse<AppRating>>;
17
+ create(appId: string, payload: CreateAppRatingInput): Promise<ServiceResponse<AppRatingMutationResult>>;
18
+ update(
19
+ appId: string,
20
+ ratingId: string,
21
+ payload: UpdateAppRatingInput
22
+ ): Promise<ServiceResponse<AppRatingMutationResult>>;
23
+ remove(appId: string, ratingId: string): Promise<ServiceResponse<AppRatingStatsResult>>;
24
+ }
25
+
26
+ class AppRatingsRemoteDataSourceImpl extends BaseRemote implements AppRatingsRemoteDataSource {
27
+ async list(appId: string, query?: ListAppRatingsQuery): Promise<ServiceResponse<AppRatingList>> {
28
+ const params = query ? { ...query } : undefined;
29
+ const { data } = await api.get<ServiceResponse<AppRatingList>>(
30
+ `/v1/apps/${encodeURIComponent(appId)}/ratings`,
31
+ { params }
32
+ );
33
+ return data;
34
+ }
35
+
36
+ async getById(appId: string, ratingId: string): Promise<ServiceResponse<AppRating>> {
37
+ const { data } = await api.get<ServiceResponse<AppRating>>(
38
+ `/v1/apps/${encodeURIComponent(appId)}/ratings/${encodeURIComponent(ratingId)}`
39
+ );
40
+ return data;
41
+ }
42
+
43
+ async create(
44
+ appId: string,
45
+ payload: CreateAppRatingInput
46
+ ): Promise<ServiceResponse<AppRatingMutationResult>> {
47
+ const { data } = await api.post<ServiceResponse<AppRatingMutationResult>>(
48
+ `/v1/apps/${encodeURIComponent(appId)}/ratings`,
49
+ payload
50
+ );
51
+ return data;
52
+ }
53
+
54
+ async update(
55
+ appId: string,
56
+ ratingId: string,
57
+ payload: UpdateAppRatingInput
58
+ ): Promise<ServiceResponse<AppRatingMutationResult>> {
59
+ const { data } = await api.patch<ServiceResponse<AppRatingMutationResult>>(
60
+ `/v1/apps/${encodeURIComponent(appId)}/ratings/${encodeURIComponent(ratingId)}`,
61
+ payload
62
+ );
63
+ return data;
64
+ }
65
+
66
+ async remove(appId: string, ratingId: string): Promise<ServiceResponse<AppRatingStatsResult>> {
67
+ const { data } = await api.delete<ServiceResponse<AppRatingStatsResult>>(
68
+ `/v1/apps/${encodeURIComponent(appId)}/ratings/${encodeURIComponent(ratingId)}`
69
+ );
70
+ return data;
71
+ }
72
+ }
73
+
74
+ export const appRatingsRemoteDataSource: AppRatingsRemoteDataSource = new AppRatingsRemoteDataSourceImpl();
75
+
76
+
@@ -0,0 +1,63 @@
1
+ import type { AppRatingsRemoteDataSource } from './remote';
2
+ import { appRatingsRemoteDataSource } from './remote';
3
+ import type {
4
+ AppRating,
5
+ AppRatingList,
6
+ AppRatingMutationResult,
7
+ AppRatingStatsResult,
8
+ CreateAppRatingInput,
9
+ ListAppRatingsQuery,
10
+ UpdateAppRatingInput,
11
+ } from './types';
12
+ import { BaseRepository } from '../../data/base-repository';
13
+
14
+ export interface AppRatingsRepository {
15
+ list(appId: string, query?: ListAppRatingsQuery): Promise<AppRatingList>;
16
+ getById(appId: string, ratingId: string): Promise<AppRating>;
17
+ create(appId: string, payload: CreateAppRatingInput): Promise<AppRatingMutationResult>;
18
+ update(
19
+ appId: string,
20
+ ratingId: string,
21
+ payload: UpdateAppRatingInput
22
+ ): Promise<AppRatingMutationResult>;
23
+ remove(appId: string, ratingId: string): Promise<AppRatingStatsResult>;
24
+ }
25
+
26
+ class AppRatingsRepositoryImpl extends BaseRepository implements AppRatingsRepository {
27
+ constructor(private readonly remote: AppRatingsRemoteDataSource) {
28
+ super();
29
+ }
30
+
31
+ async list(appId: string, query?: ListAppRatingsQuery): Promise<AppRatingList> {
32
+ const res = await this.remote.list(appId, query);
33
+ return this.unwrapOrThrow(res);
34
+ }
35
+
36
+ async getById(appId: string, ratingId: string): Promise<AppRating> {
37
+ const res = await this.remote.getById(appId, ratingId);
38
+ return this.unwrapOrThrow(res);
39
+ }
40
+
41
+ async create(appId: string, payload: CreateAppRatingInput): Promise<AppRatingMutationResult> {
42
+ const res = await this.remote.create(appId, payload);
43
+ return this.unwrapOrThrow(res);
44
+ }
45
+
46
+ async update(
47
+ appId: string,
48
+ ratingId: string,
49
+ payload: UpdateAppRatingInput
50
+ ): Promise<AppRatingMutationResult> {
51
+ const res = await this.remote.update(appId, ratingId, payload);
52
+ return this.unwrapOrThrow(res);
53
+ }
54
+
55
+ async remove(appId: string, ratingId: string): Promise<AppRatingStatsResult> {
56
+ const res = await this.remote.remove(appId, ratingId);
57
+ return this.unwrapOrThrow(res);
58
+ }
59
+ }
60
+
61
+ export const appRatingsRepository: AppRatingsRepository = new AppRatingsRepositoryImpl(appRatingsRemoteDataSource);
62
+
63
+
@@ -0,0 +1,57 @@
1
+ export type AppRating = {
2
+ id: string;
3
+ appId: string;
4
+ userId: string;
5
+ rating: number;
6
+ commentId: string | null;
7
+ title: string | null;
8
+ review: string | null;
9
+ metadata: Record<string, unknown>;
10
+ createdAt: string;
11
+ updatedAt: string;
12
+ };
13
+
14
+ export type ListAppRatingsQuery = {
15
+ page?: number;
16
+ pageSize?: number;
17
+ userId?: string;
18
+ minRating?: number;
19
+ maxRating?: number;
20
+ };
21
+
22
+ export type CreateAppRatingInput = {
23
+ rating: number;
24
+ commentId?: string | null;
25
+ title?: string | null;
26
+ review?: string | null;
27
+ metadata?: Record<string, unknown>;
28
+ };
29
+
30
+ export type UpdateAppRatingInput = Partial<CreateAppRatingInput>;
31
+
32
+ export type AppRatingStats = {
33
+ total?: number;
34
+ average?: number | null;
35
+ };
36
+
37
+ export type AppRatingList = {
38
+ items: AppRating[];
39
+ pageInfo: {
40
+ page: number;
41
+ pageSize: number;
42
+ total: number;
43
+ hasMore: boolean;
44
+ };
45
+ stats: AppRatingStats;
46
+ };
47
+
48
+ export type AppRatingMutationResult = {
49
+ rating: AppRating;
50
+ stats?: AppRatingStats;
51
+ };
52
+
53
+ export type AppRatingStatsResult = {
54
+ stats?: AppRatingStats;
55
+ };
56
+
57
+
@@ -0,0 +1,40 @@
1
+ import { api } from '../../core/services/http';
2
+ import type { ServiceResponse } from '../types';
3
+ import { BaseRemote } from '../base-remote';
4
+ import type { Thread, UpdateThreadRequest } from './types';
5
+
6
+ export interface ThreadsRemoteDataSource {
7
+ list(): Promise<ServiceResponse<Thread[]>>;
8
+ getById(threadId: string): Promise<ServiceResponse<Thread>>;
9
+ update(threadId: string, payload: UpdateThreadRequest): Promise<ServiceResponse<Thread>>;
10
+ delete(threadId: string): Promise<ServiceResponse<never>>;
11
+ }
12
+
13
+ class ThreadsRemoteDataSourceImpl extends BaseRemote implements ThreadsRemoteDataSource {
14
+ async list(): Promise<ServiceResponse<Thread[]>> {
15
+ const { data } = await api.get<ServiceResponse<Thread[]>>('/v1/threads');
16
+ return data;
17
+ }
18
+
19
+ async getById(threadId: string): Promise<ServiceResponse<Thread>> {
20
+ const { data } = await api.get<ServiceResponse<Thread>>(`/v1/threads/${encodeURIComponent(threadId)}`);
21
+ return data;
22
+ }
23
+
24
+ async update(threadId: string, payload: UpdateThreadRequest): Promise<ServiceResponse<Thread>> {
25
+ const { data } = await api.patch<ServiceResponse<Thread>>(
26
+ `/v1/threads/${encodeURIComponent(threadId)}`,
27
+ payload
28
+ );
29
+ return data;
30
+ }
31
+
32
+ async delete(threadId: string): Promise<ServiceResponse<never>> {
33
+ const { data } = await api.delete<ServiceResponse<never>>(`/v1/threads/${encodeURIComponent(threadId)}`);
34
+ return data;
35
+ }
36
+ }
37
+
38
+ export const threadsRemoteDataSource: ThreadsRemoteDataSource = new ThreadsRemoteDataSourceImpl();
39
+
40
+
@@ -0,0 +1,41 @@
1
+ import type { ThreadsRemoteDataSource } from './remote';
2
+ import { threadsRemoteDataSource } from './remote';
3
+ import type { Thread, UpdateThreadRequest } from './types';
4
+ import { BaseRepository } from '../../data/base-repository';
5
+
6
+ export interface ThreadsRepository {
7
+ list(): Promise<Thread[]>;
8
+ getById(threadId: string): Promise<Thread>;
9
+ update(threadId: string, payload: UpdateThreadRequest): Promise<Thread>;
10
+ delete(threadId: string): Promise<void>;
11
+ }
12
+
13
+ class ThreadsRepositoryImpl extends BaseRepository implements ThreadsRepository {
14
+ constructor(private readonly remote: ThreadsRemoteDataSource) {
15
+ super();
16
+ }
17
+
18
+ async list(): Promise<Thread[]> {
19
+ const res = await this.remote.list();
20
+ return this.unwrapOrThrow(res);
21
+ }
22
+
23
+ async getById(threadId: string): Promise<Thread> {
24
+ const res = await this.remote.getById(threadId);
25
+ return this.unwrapOrThrow(res);
26
+ }
27
+
28
+ async update(threadId: string, payload: UpdateThreadRequest): Promise<Thread> {
29
+ const res = await this.remote.update(threadId, payload);
30
+ return this.unwrapOrThrow(res);
31
+ }
32
+
33
+ async delete(threadId: string): Promise<void> {
34
+ const res = await this.remote.delete(threadId);
35
+ this.unwrapOrThrow(res as any);
36
+ }
37
+ }
38
+
39
+ export const threadsRepository: ThreadsRepository = new ThreadsRepositoryImpl(threadsRemoteDataSource);
40
+
41
+
@@ -0,0 +1,25 @@
1
+ export type ThreadKind = 'chat' | 'review' | 'build' | 'system';
2
+
3
+ export type Thread = {
4
+ id: string;
5
+ createdBy: string;
6
+ title: string | null;
7
+ kind: ThreadKind;
8
+ isLocked: boolean;
9
+ isArchived: boolean;
10
+ metadata: Record<string, unknown> | null;
11
+ messageCount: number;
12
+ lastMessageAt: string | null;
13
+ createdAt: string;
14
+ updatedAt: string;
15
+ };
16
+
17
+ export type UpdateThreadRequest = {
18
+ title?: string;
19
+ kind?: ThreadKind;
20
+ isLocked?: boolean;
21
+ isArchived?: boolean;
22
+ metadata?: Record<string, unknown>;
23
+ };
24
+
25
+