@comergehq/studio 0.1.19 → 0.1.21

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.mjs CHANGED
@@ -519,6 +519,12 @@ var AppsRemoteDataSourceImpl = class extends BaseRemote {
519
519
  );
520
520
  return data;
521
521
  }
522
+ async syncUpstream(appId) {
523
+ const { data } = await api.post(
524
+ `/v1/apps/${encodeURIComponent(appId)}/sync-upstream`
525
+ );
526
+ return data;
527
+ }
522
528
  };
523
529
  var appsRemoteDataSource = new AppsRemoteDataSourceImpl();
524
530
 
@@ -597,6 +603,10 @@ var AppsRepositoryImpl = class extends BaseRepository {
597
603
  const res = await this.remote.importFromGithub(payload);
598
604
  return this.unwrapOrThrow(res);
599
605
  }
606
+ async syncUpstream(appId) {
607
+ const res = await this.remote.syncUpstream(appId);
608
+ return this.unwrapOrThrow(res);
609
+ }
600
610
  subscribeCreatedApps(userId, handlers) {
601
611
  if (!userId) return () => {
602
612
  };
@@ -4872,7 +4882,7 @@ function PreviewCustomizeSection({
4872
4882
  // src/studio/ui/preview-panel/PreviewCollaborateSection.tsx
4873
4883
  import * as React32 from "react";
4874
4884
  import { ActivityIndicator as ActivityIndicator6, Alert, View as View32 } from "react-native";
4875
- import { Send as Send2 } from "lucide-react-native";
4885
+ import { RefreshCw, Send as Send2 } from "lucide-react-native";
4876
4886
 
4877
4887
  // src/components/merge-requests/MergeRequestStatusCard.tsx
4878
4888
  import * as React28 from "react";
@@ -5417,6 +5427,9 @@ function ReviewMergeRequestCarousel({
5417
5427
  import { Fragment as Fragment5, jsx as jsx44, jsxs as jsxs25 } from "react/jsx-runtime";
5418
5428
  function PreviewCollaborateSection({
5419
5429
  canSubmitMergeRequest,
5430
+ canSyncUpstream,
5431
+ syncingUpstream,
5432
+ upstreamSyncStatus,
5420
5433
  incomingMergeRequests,
5421
5434
  outgoingMergeRequests,
5422
5435
  creatorStatsById,
@@ -5425,15 +5438,18 @@ function PreviewCollaborateSection({
5425
5438
  testingMrId,
5426
5439
  toMergeRequestSummary,
5427
5440
  onSubmitMergeRequest,
5441
+ onSyncUpstream,
5428
5442
  onRequestApprove,
5429
5443
  onReject,
5430
5444
  onTestMr
5431
5445
  }) {
5432
5446
  const theme = useTheme();
5433
5447
  const [submittingMr, setSubmittingMr] = React32.useState(false);
5434
- const hasSection = canSubmitMergeRequest || incomingMergeRequests.length > 0 || outgoingMergeRequests.length > 0;
5448
+ const [syncingLocal, setSyncingLocal] = React32.useState(false);
5449
+ const hasSection = canSubmitMergeRequest || canSyncUpstream || incomingMergeRequests.length > 0 || outgoingMergeRequests.length > 0;
5435
5450
  if (!hasSection) return null;
5436
- const showActionsSubtitle = canSubmitMergeRequest && onSubmitMergeRequest || onTestMr && incomingMergeRequests.length > 0;
5451
+ const isSyncing = Boolean(syncingUpstream || syncingLocal);
5452
+ const showActionsSubtitle = canSubmitMergeRequest && onSubmitMergeRequest || canSyncUpstream && onSyncUpstream || onTestMr && incomingMergeRequests.length > 0;
5437
5453
  return /* @__PURE__ */ jsxs25(Fragment5, { children: [
5438
5454
  /* @__PURE__ */ jsx44(SectionTitle, { marginTop: theme.spacing.xl, children: "Collaborate" }),
5439
5455
  showActionsSubtitle ? /* @__PURE__ */ jsx44(
@@ -5502,6 +5518,64 @@ function PreviewCollaborateSection({
5502
5518
  right: /* @__PURE__ */ jsx44(Send2, { size: 16, color: "#03DAC6" })
5503
5519
  }
5504
5520
  ) : null,
5521
+ canSyncUpstream && onSyncUpstream ? /* @__PURE__ */ jsx44(
5522
+ PressableCardRow,
5523
+ {
5524
+ accessibilityLabel: "Sync from original",
5525
+ disabled: isSyncing,
5526
+ onPress: () => {
5527
+ Alert.alert(
5528
+ "Sync from Original",
5529
+ "This will pull the latest upstream changes into your remix.",
5530
+ [
5531
+ { text: "Cancel", style: "cancel" },
5532
+ {
5533
+ text: "Sync",
5534
+ style: "destructive",
5535
+ onPress: () => {
5536
+ setSyncingLocal(true);
5537
+ Promise.resolve(onSyncUpstream()).then((result) => {
5538
+ if ((result == null ? void 0 : result.status) === "up-to-date") {
5539
+ Alert.alert("Up to date", "Your remix already includes the latest upstream changes.");
5540
+ } else {
5541
+ Alert.alert("Sync started", "Upstream changes are being merged into your remix.");
5542
+ }
5543
+ }).catch(() => {
5544
+ Alert.alert("Sync failed", "We could not start the sync. Please try again.");
5545
+ }).finally(() => setSyncingLocal(false));
5546
+ }
5547
+ }
5548
+ ]
5549
+ );
5550
+ },
5551
+ style: {
5552
+ padding: theme.spacing.lg,
5553
+ borderRadius: theme.radii.lg,
5554
+ backgroundColor: withAlpha(theme.colors.surfaceRaised, 0.5),
5555
+ borderWidth: 1,
5556
+ borderColor: withAlpha(theme.colors.primary, 0.25),
5557
+ marginBottom: theme.spacing.sm
5558
+ },
5559
+ left: /* @__PURE__ */ jsx44(
5560
+ View32,
5561
+ {
5562
+ style: {
5563
+ width: 40,
5564
+ height: 40,
5565
+ borderRadius: 999,
5566
+ alignItems: "center",
5567
+ justifyContent: "center",
5568
+ backgroundColor: withAlpha(theme.colors.primary, 0.12),
5569
+ marginRight: theme.spacing.lg
5570
+ },
5571
+ children: isSyncing ? /* @__PURE__ */ jsx44(ActivityIndicator6, { color: theme.colors.primary, size: "small" }) : /* @__PURE__ */ jsx44(RefreshCw, { size: 18, color: theme.colors.primary })
5572
+ }
5573
+ ),
5574
+ title: /* @__PURE__ */ jsx44(Text, { style: { color: theme.colors.text, fontSize: 16, lineHeight: 20, fontWeight: theme.typography.fontWeight.semibold }, children: "Sync from Original" }),
5575
+ subtitle: /* @__PURE__ */ jsx44(Text, { style: { color: theme.colors.textMuted, fontSize: 12, lineHeight: 16, marginTop: 2 }, children: isSyncing ? "Syncing upstream changes..." : upstreamSyncStatus === "up-to-date" ? "You are already up to date with the original app" : "Pull the latest upstream changes into this remix" }),
5576
+ right: /* @__PURE__ */ jsx44(RefreshCw, { size: 16, color: theme.colors.primary })
5577
+ }
5578
+ ) : null,
5505
5579
  onTestMr && incomingMergeRequests.length > 0 ? /* @__PURE__ */ jsx44(
5506
5580
  ReviewMergeRequestCarousel,
5507
5581
  {
@@ -5809,6 +5883,12 @@ function usePreviewPanelData(params) {
5809
5883
  if (app.headCommitId && app.forkedFromCommitId && app.headCommitId !== app.forkedFromCommitId) return true;
5810
5884
  return false;
5811
5885
  }, [app, isOwner, outgoingMergeRequests]);
5886
+ const canSyncUpstream = React34.useMemo(() => {
5887
+ if (!isOwner) return false;
5888
+ if (!app) return false;
5889
+ if (!app.forkedFromAppId) return false;
5890
+ return app.status === "ready";
5891
+ }, [app, isOwner]);
5812
5892
  const showProcessing = app ? app.status !== "ready" : false;
5813
5893
  return {
5814
5894
  imageUrl,
@@ -5818,7 +5898,8 @@ function usePreviewPanelData(params) {
5818
5898
  insights,
5819
5899
  stats,
5820
5900
  showProcessing,
5821
- canSubmitMergeRequest
5901
+ canSubmitMergeRequest,
5902
+ canSyncUpstream
5822
5903
  };
5823
5904
  }
5824
5905
 
@@ -5841,6 +5922,9 @@ function PreviewPanel({
5841
5922
  onGoToChat,
5842
5923
  onStartDraw,
5843
5924
  onSubmitMergeRequest,
5925
+ onSyncUpstream,
5926
+ syncingUpstream,
5927
+ upstreamSyncStatus,
5844
5928
  onRequestApprove,
5845
5929
  onReject,
5846
5930
  onTestMr,
@@ -5850,11 +5934,11 @@ function PreviewPanel({
5850
5934
  const handleShare = React35.useCallback(async () => {
5851
5935
  if (!app || !app.isPublic) return;
5852
5936
  const shareUrl = `https://remix.one/app/${app.id}`;
5853
- const message = app.name ? `${app.name} on Comerge
5854
- ${shareUrl}` : `Check out this app on Comerge
5937
+ const message = app.name ? `${app.name} on Remix
5938
+ ${shareUrl}` : `Check out this app on Remix
5855
5939
  ${shareUrl}`;
5856
5940
  try {
5857
- const title = app.name ?? "Comerge app";
5941
+ const title = app.name ?? "Remix app";
5858
5942
  const payload = Platform8.OS === "ios" ? {
5859
5943
  title,
5860
5944
  message
@@ -5868,7 +5952,17 @@ ${shareUrl}`;
5868
5952
  log.warn("PreviewPanel share failed", error);
5869
5953
  }
5870
5954
  }, [app]);
5871
- const { imageUrl, imageLoaded, setImageLoaded, creator, insights, stats, showProcessing, canSubmitMergeRequest } = usePreviewPanelData({
5955
+ const {
5956
+ imageUrl,
5957
+ imageLoaded,
5958
+ setImageLoaded,
5959
+ creator,
5960
+ insights,
5961
+ stats,
5962
+ showProcessing,
5963
+ canSubmitMergeRequest,
5964
+ canSyncUpstream
5965
+ } = usePreviewPanelData({
5872
5966
  app,
5873
5967
  isOwner,
5874
5968
  outgoingMergeRequests,
@@ -5928,6 +6022,9 @@ ${shareUrl}`;
5928
6022
  PreviewCollaborateSection,
5929
6023
  {
5930
6024
  canSubmitMergeRequest,
6025
+ canSyncUpstream,
6026
+ syncingUpstream,
6027
+ upstreamSyncStatus,
5931
6028
  incomingMergeRequests,
5932
6029
  outgoingMergeRequests,
5933
6030
  creatorStatsById,
@@ -5936,6 +6033,7 @@ ${shareUrl}`;
5936
6033
  testingMrId,
5937
6034
  toMergeRequestSummary,
5938
6035
  onSubmitMergeRequest,
6036
+ onSyncUpstream,
5939
6037
  onRequestApprove,
5940
6038
  onReject,
5941
6039
  onTestMr
@@ -5970,6 +6068,8 @@ function ChatMessageBubble({ message, renderContent, style }) {
5970
6068
  const isMergeApproved = metaEvent === "merge_request.approved";
5971
6069
  const isMergeRejected = metaEvent === "merge_request.rejected";
5972
6070
  const isMergeCompleted = metaEvent === "merge.completed";
6071
+ const isSyncStarted = metaEvent === "sync.started";
6072
+ const isSyncCompleted = metaEvent === "sync.completed";
5973
6073
  const isHuman = message.author === "human" || isMergeApproved || isMergeRejected;
5974
6074
  const align = { alignSelf: isHuman ? "flex-end" : "flex-start" };
5975
6075
  const bubbleVariant = isHuman ? "surface" : "surfaceRaised";
@@ -5991,8 +6091,8 @@ function ChatMessageBubble({ message, renderContent, style }) {
5991
6091
  cornerStyle
5992
6092
  ],
5993
6093
  children: /* @__PURE__ */ jsxs27(View34, { style: { flexDirection: "row", alignItems: "center" }, children: [
5994
- isMergeCompleted ? /* @__PURE__ */ jsx46(CheckCheck2, { size: 16, color: theme.colors.success, style: { marginRight: theme.spacing.sm } }) : null,
5995
- isMergeApproved ? /* @__PURE__ */ jsx46(GitMerge2, { size: 16, color: theme.colors.text, style: { marginRight: theme.spacing.sm } }) : null,
6094
+ isMergeCompleted || isSyncCompleted ? /* @__PURE__ */ jsx46(CheckCheck2, { size: 16, color: theme.colors.success, style: { marginRight: theme.spacing.sm } }) : null,
6095
+ isMergeApproved || isSyncStarted ? /* @__PURE__ */ jsx46(GitMerge2, { size: 16, color: theme.colors.text, style: { marginRight: theme.spacing.sm } }) : null,
5996
6096
  /* @__PURE__ */ jsx46(View34, { style: { flexShrink: 1, minWidth: 0 }, children: renderContent ? renderContent(message) : /* @__PURE__ */ jsx46(MarkdownText, { markdown: message.content, variant: "chat", bodyColor }) })
5997
6097
  ] })
5998
6098
  }
@@ -6962,6 +7062,9 @@ function StudioOverlay({
6962
7062
  testingMrId,
6963
7063
  toMergeRequestSummary,
6964
7064
  onSubmitMergeRequest,
7065
+ onSyncUpstream,
7066
+ syncingUpstream,
7067
+ upstreamSyncStatus,
6965
7068
  onApprove,
6966
7069
  onReject,
6967
7070
  onTestMr,
@@ -7106,6 +7209,9 @@ function StudioOverlay({
7106
7209
  onGoToChat: goToChat,
7107
7210
  onStartDraw: isOwner ? startDraw : void 0,
7108
7211
  onSubmitMergeRequest,
7212
+ onSyncUpstream,
7213
+ syncingUpstream,
7214
+ upstreamSyncStatus,
7109
7215
  onRequestApprove: (mr) => setConfirmMrId(mr.id),
7110
7216
  onReject,
7111
7217
  onTestMr: handleTestMr,
@@ -7534,6 +7640,8 @@ function ComergeStudioInner({
7534
7640
  const chatSendDisabled = false;
7535
7641
  const [processingMrId, setProcessingMrId] = React47.useState(null);
7536
7642
  const [testingMrId, setTestingMrId] = React47.useState(null);
7643
+ const [syncingUpstream, setSyncingUpstream] = React47.useState(false);
7644
+ const [upstreamSyncStatus, setUpstreamSyncStatus] = React47.useState(null);
7537
7645
  const chatShowTypingIndicator = React47.useMemo(() => {
7538
7646
  var _a;
7539
7647
  if (!thread.raw || thread.raw.length === 0) return false;
@@ -7544,7 +7652,21 @@ function ComergeStudioInner({
7544
7652
  React47.useEffect(() => {
7545
7653
  updateLastEditQueueInfo(null);
7546
7654
  setSuppressQueueUntilResponse(false);
7655
+ setUpstreamSyncStatus(null);
7547
7656
  }, [activeAppId, updateLastEditQueueInfo]);
7657
+ const handleSyncUpstream = React47.useCallback(async () => {
7658
+ if (!(app == null ? void 0 : app.id)) {
7659
+ throw new Error("Missing app");
7660
+ }
7661
+ setSyncingUpstream(true);
7662
+ try {
7663
+ const result = await appsRepository.syncUpstream(activeAppId);
7664
+ setUpstreamSyncStatus(result.status);
7665
+ return result;
7666
+ } finally {
7667
+ setSyncingUpstream(false);
7668
+ }
7669
+ }, [activeAppId, app == null ? void 0 : app.id]);
7548
7670
  React47.useEffect(() => {
7549
7671
  if (!(lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId)) return;
7550
7672
  const stillPresent = editQueue.items.some((item) => item.id === lastEditQueueInfo.queueItemId);
@@ -7600,6 +7722,9 @@ function ComergeStudioInner({
7600
7722
  onSubmitMergeRequest: (app == null ? void 0 : app.forkedFromAppId) && actions.isOwner && !hasOpenOutgoingMr ? async () => {
7601
7723
  await mergeRequests.actions.openMergeRequest(activeAppId);
7602
7724
  } : void 0,
7725
+ onSyncUpstream: actions.isOwner && (app == null ? void 0 : app.forkedFromAppId) ? handleSyncUpstream : void 0,
7726
+ syncingUpstream,
7727
+ upstreamSyncStatus,
7603
7728
  onApprove: async (mr) => {
7604
7729
  if (processingMrId) return;
7605
7730
  setProcessingMrId(mr.id);