@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.js CHANGED
@@ -549,6 +549,12 @@ var AppsRemoteDataSourceImpl = class extends BaseRemote {
549
549
  );
550
550
  return data;
551
551
  }
552
+ async syncUpstream(appId) {
553
+ const { data } = await api.post(
554
+ `/v1/apps/${encodeURIComponent(appId)}/sync-upstream`
555
+ );
556
+ return data;
557
+ }
552
558
  };
553
559
  var appsRemoteDataSource = new AppsRemoteDataSourceImpl();
554
560
 
@@ -627,6 +633,10 @@ var AppsRepositoryImpl = class extends BaseRepository {
627
633
  const res = await this.remote.importFromGithub(payload);
628
634
  return this.unwrapOrThrow(res);
629
635
  }
636
+ async syncUpstream(appId) {
637
+ const res = await this.remote.syncUpstream(appId);
638
+ return this.unwrapOrThrow(res);
639
+ }
630
640
  subscribeCreatedApps(userId, handlers) {
631
641
  if (!userId) return () => {
632
642
  };
@@ -5390,6 +5400,9 @@ function ReviewMergeRequestCarousel({
5390
5400
  var import_jsx_runtime44 = require("react/jsx-runtime");
5391
5401
  function PreviewCollaborateSection({
5392
5402
  canSubmitMergeRequest,
5403
+ canSyncUpstream,
5404
+ syncingUpstream,
5405
+ upstreamSyncStatus,
5393
5406
  incomingMergeRequests,
5394
5407
  outgoingMergeRequests,
5395
5408
  creatorStatsById,
@@ -5398,15 +5411,18 @@ function PreviewCollaborateSection({
5398
5411
  testingMrId,
5399
5412
  toMergeRequestSummary,
5400
5413
  onSubmitMergeRequest,
5414
+ onSyncUpstream,
5401
5415
  onRequestApprove,
5402
5416
  onReject,
5403
5417
  onTestMr
5404
5418
  }) {
5405
5419
  const theme = useTheme();
5406
5420
  const [submittingMr, setSubmittingMr] = React32.useState(false);
5407
- const hasSection = canSubmitMergeRequest || incomingMergeRequests.length > 0 || outgoingMergeRequests.length > 0;
5421
+ const [syncingLocal, setSyncingLocal] = React32.useState(false);
5422
+ const hasSection = canSubmitMergeRequest || canSyncUpstream || incomingMergeRequests.length > 0 || outgoingMergeRequests.length > 0;
5408
5423
  if (!hasSection) return null;
5409
- const showActionsSubtitle = canSubmitMergeRequest && onSubmitMergeRequest || onTestMr && incomingMergeRequests.length > 0;
5424
+ const isSyncing = Boolean(syncingUpstream || syncingLocal);
5425
+ const showActionsSubtitle = canSubmitMergeRequest && onSubmitMergeRequest || canSyncUpstream && onSyncUpstream || onTestMr && incomingMergeRequests.length > 0;
5410
5426
  return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
5411
5427
  /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(SectionTitle, { marginTop: theme.spacing.xl, children: "Collaborate" }),
5412
5428
  showActionsSubtitle ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
@@ -5475,6 +5491,64 @@ function PreviewCollaborateSection({
5475
5491
  right: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react_native9.Send, { size: 16, color: "#03DAC6" })
5476
5492
  }
5477
5493
  ) : null,
5494
+ canSyncUpstream && onSyncUpstream ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
5495
+ PressableCardRow,
5496
+ {
5497
+ accessibilityLabel: "Sync from original",
5498
+ disabled: isSyncing,
5499
+ onPress: () => {
5500
+ import_react_native42.Alert.alert(
5501
+ "Sync from Original",
5502
+ "This will pull the latest upstream changes into your remix.",
5503
+ [
5504
+ { text: "Cancel", style: "cancel" },
5505
+ {
5506
+ text: "Sync",
5507
+ style: "destructive",
5508
+ onPress: () => {
5509
+ setSyncingLocal(true);
5510
+ Promise.resolve(onSyncUpstream()).then((result) => {
5511
+ if ((result == null ? void 0 : result.status) === "up-to-date") {
5512
+ import_react_native42.Alert.alert("Up to date", "Your remix already includes the latest upstream changes.");
5513
+ } else {
5514
+ import_react_native42.Alert.alert("Sync started", "Upstream changes are being merged into your remix.");
5515
+ }
5516
+ }).catch(() => {
5517
+ import_react_native42.Alert.alert("Sync failed", "We could not start the sync. Please try again.");
5518
+ }).finally(() => setSyncingLocal(false));
5519
+ }
5520
+ }
5521
+ ]
5522
+ );
5523
+ },
5524
+ style: {
5525
+ padding: theme.spacing.lg,
5526
+ borderRadius: theme.radii.lg,
5527
+ backgroundColor: withAlpha(theme.colors.surfaceRaised, 0.5),
5528
+ borderWidth: 1,
5529
+ borderColor: withAlpha(theme.colors.primary, 0.25),
5530
+ marginBottom: theme.spacing.sm
5531
+ },
5532
+ left: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
5533
+ import_react_native42.View,
5534
+ {
5535
+ style: {
5536
+ width: 40,
5537
+ height: 40,
5538
+ borderRadius: 999,
5539
+ alignItems: "center",
5540
+ justifyContent: "center",
5541
+ backgroundColor: withAlpha(theme.colors.primary, 0.12),
5542
+ marginRight: theme.spacing.lg
5543
+ },
5544
+ children: isSyncing ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_react_native42.ActivityIndicator, { color: theme.colors.primary, size: "small" }) : /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react_native9.RefreshCw, { size: 18, color: theme.colors.primary })
5545
+ }
5546
+ ),
5547
+ title: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Text, { style: { color: theme.colors.text, fontSize: 16, lineHeight: 20, fontWeight: theme.typography.fontWeight.semibold }, children: "Sync from Original" }),
5548
+ subtitle: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(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" }),
5549
+ right: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(import_lucide_react_native9.RefreshCw, { size: 16, color: theme.colors.primary })
5550
+ }
5551
+ ) : null,
5478
5552
  onTestMr && incomingMergeRequests.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
5479
5553
  ReviewMergeRequestCarousel,
5480
5554
  {
@@ -5782,6 +5856,12 @@ function usePreviewPanelData(params) {
5782
5856
  if (app.headCommitId && app.forkedFromCommitId && app.headCommitId !== app.forkedFromCommitId) return true;
5783
5857
  return false;
5784
5858
  }, [app, isOwner, outgoingMergeRequests]);
5859
+ const canSyncUpstream = React34.useMemo(() => {
5860
+ if (!isOwner) return false;
5861
+ if (!app) return false;
5862
+ if (!app.forkedFromAppId) return false;
5863
+ return app.status === "ready";
5864
+ }, [app, isOwner]);
5785
5865
  const showProcessing = app ? app.status !== "ready" : false;
5786
5866
  return {
5787
5867
  imageUrl,
@@ -5791,7 +5871,8 @@ function usePreviewPanelData(params) {
5791
5871
  insights,
5792
5872
  stats,
5793
5873
  showProcessing,
5794
- canSubmitMergeRequest
5874
+ canSubmitMergeRequest,
5875
+ canSyncUpstream
5795
5876
  };
5796
5877
  }
5797
5878
 
@@ -5814,6 +5895,9 @@ function PreviewPanel({
5814
5895
  onGoToChat,
5815
5896
  onStartDraw,
5816
5897
  onSubmitMergeRequest,
5898
+ onSyncUpstream,
5899
+ syncingUpstream,
5900
+ upstreamSyncStatus,
5817
5901
  onRequestApprove,
5818
5902
  onReject,
5819
5903
  onTestMr,
@@ -5823,11 +5907,11 @@ function PreviewPanel({
5823
5907
  const handleShare = React35.useCallback(async () => {
5824
5908
  if (!app || !app.isPublic) return;
5825
5909
  const shareUrl = `https://remix.one/app/${app.id}`;
5826
- const message = app.name ? `${app.name} on Comerge
5827
- ${shareUrl}` : `Check out this app on Comerge
5910
+ const message = app.name ? `${app.name} on Remix
5911
+ ${shareUrl}` : `Check out this app on Remix
5828
5912
  ${shareUrl}`;
5829
5913
  try {
5830
- const title = app.name ?? "Comerge app";
5914
+ const title = app.name ?? "Remix app";
5831
5915
  const payload = import_react_native43.Platform.OS === "ios" ? {
5832
5916
  title,
5833
5917
  message
@@ -5841,7 +5925,17 @@ ${shareUrl}`;
5841
5925
  log.warn("PreviewPanel share failed", error);
5842
5926
  }
5843
5927
  }, [app]);
5844
- const { imageUrl, imageLoaded, setImageLoaded, creator, insights, stats, showProcessing, canSubmitMergeRequest } = usePreviewPanelData({
5928
+ const {
5929
+ imageUrl,
5930
+ imageLoaded,
5931
+ setImageLoaded,
5932
+ creator,
5933
+ insights,
5934
+ stats,
5935
+ showProcessing,
5936
+ canSubmitMergeRequest,
5937
+ canSyncUpstream
5938
+ } = usePreviewPanelData({
5845
5939
  app,
5846
5940
  isOwner,
5847
5941
  outgoingMergeRequests,
@@ -5901,6 +5995,9 @@ ${shareUrl}`;
5901
5995
  PreviewCollaborateSection,
5902
5996
  {
5903
5997
  canSubmitMergeRequest,
5998
+ canSyncUpstream,
5999
+ syncingUpstream,
6000
+ upstreamSyncStatus,
5904
6001
  incomingMergeRequests,
5905
6002
  outgoingMergeRequests,
5906
6003
  creatorStatsById,
@@ -5909,6 +6006,7 @@ ${shareUrl}`;
5909
6006
  testingMrId,
5910
6007
  toMergeRequestSummary,
5911
6008
  onSubmitMergeRequest,
6009
+ onSyncUpstream,
5912
6010
  onRequestApprove,
5913
6011
  onReject,
5914
6012
  onTestMr
@@ -5943,6 +6041,8 @@ function ChatMessageBubble({ message, renderContent, style }) {
5943
6041
  const isMergeApproved = metaEvent === "merge_request.approved";
5944
6042
  const isMergeRejected = metaEvent === "merge_request.rejected";
5945
6043
  const isMergeCompleted = metaEvent === "merge.completed";
6044
+ const isSyncStarted = metaEvent === "sync.started";
6045
+ const isSyncCompleted = metaEvent === "sync.completed";
5946
6046
  const isHuman = message.author === "human" || isMergeApproved || isMergeRejected;
5947
6047
  const align = { alignSelf: isHuman ? "flex-end" : "flex-start" };
5948
6048
  const bubbleVariant = isHuman ? "surface" : "surfaceRaised";
@@ -5964,8 +6064,8 @@ function ChatMessageBubble({ message, renderContent, style }) {
5964
6064
  cornerStyle
5965
6065
  ],
5966
6066
  children: /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(import_react_native44.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
5967
- isMergeCompleted ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react_native10.CheckCheck, { size: 16, color: theme.colors.success, style: { marginRight: theme.spacing.sm } }) : null,
5968
- isMergeApproved ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react_native10.GitMerge, { size: 16, color: theme.colors.text, style: { marginRight: theme.spacing.sm } }) : null,
6067
+ isMergeCompleted || isSyncCompleted ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react_native10.CheckCheck, { size: 16, color: theme.colors.success, style: { marginRight: theme.spacing.sm } }) : null,
6068
+ isMergeApproved || isSyncStarted ? /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_lucide_react_native10.GitMerge, { size: 16, color: theme.colors.text, style: { marginRight: theme.spacing.sm } }) : null,
5969
6069
  /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(import_react_native44.View, { style: { flexShrink: 1, minWidth: 0 }, children: renderContent ? renderContent(message) : /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(MarkdownText, { markdown: message.content, variant: "chat", bodyColor }) })
5970
6070
  ] })
5971
6071
  }
@@ -6928,6 +7028,9 @@ function StudioOverlay({
6928
7028
  testingMrId,
6929
7029
  toMergeRequestSummary,
6930
7030
  onSubmitMergeRequest,
7031
+ onSyncUpstream,
7032
+ syncingUpstream,
7033
+ upstreamSyncStatus,
6931
7034
  onApprove,
6932
7035
  onReject,
6933
7036
  onTestMr,
@@ -7072,6 +7175,9 @@ function StudioOverlay({
7072
7175
  onGoToChat: goToChat,
7073
7176
  onStartDraw: isOwner ? startDraw : void 0,
7074
7177
  onSubmitMergeRequest,
7178
+ onSyncUpstream,
7179
+ syncingUpstream,
7180
+ upstreamSyncStatus,
7075
7181
  onRequestApprove: (mr) => setConfirmMrId(mr.id),
7076
7182
  onReject,
7077
7183
  onTestMr: handleTestMr,
@@ -7500,6 +7606,8 @@ function ComergeStudioInner({
7500
7606
  const chatSendDisabled = false;
7501
7607
  const [processingMrId, setProcessingMrId] = React47.useState(null);
7502
7608
  const [testingMrId, setTestingMrId] = React47.useState(null);
7609
+ const [syncingUpstream, setSyncingUpstream] = React47.useState(false);
7610
+ const [upstreamSyncStatus, setUpstreamSyncStatus] = React47.useState(null);
7503
7611
  const chatShowTypingIndicator = React47.useMemo(() => {
7504
7612
  var _a;
7505
7613
  if (!thread.raw || thread.raw.length === 0) return false;
@@ -7510,7 +7618,21 @@ function ComergeStudioInner({
7510
7618
  React47.useEffect(() => {
7511
7619
  updateLastEditQueueInfo(null);
7512
7620
  setSuppressQueueUntilResponse(false);
7621
+ setUpstreamSyncStatus(null);
7513
7622
  }, [activeAppId, updateLastEditQueueInfo]);
7623
+ const handleSyncUpstream = React47.useCallback(async () => {
7624
+ if (!(app == null ? void 0 : app.id)) {
7625
+ throw new Error("Missing app");
7626
+ }
7627
+ setSyncingUpstream(true);
7628
+ try {
7629
+ const result = await appsRepository.syncUpstream(activeAppId);
7630
+ setUpstreamSyncStatus(result.status);
7631
+ return result;
7632
+ } finally {
7633
+ setSyncingUpstream(false);
7634
+ }
7635
+ }, [activeAppId, app == null ? void 0 : app.id]);
7514
7636
  React47.useEffect(() => {
7515
7637
  if (!(lastEditQueueInfo == null ? void 0 : lastEditQueueInfo.queueItemId)) return;
7516
7638
  const stillPresent = editQueue.items.some((item) => item.id === lastEditQueueInfo.queueItemId);
@@ -7566,6 +7688,9 @@ function ComergeStudioInner({
7566
7688
  onSubmitMergeRequest: (app == null ? void 0 : app.forkedFromAppId) && actions.isOwner && !hasOpenOutgoingMr ? async () => {
7567
7689
  await mergeRequests.actions.openMergeRequest(activeAppId);
7568
7690
  } : void 0,
7691
+ onSyncUpstream: actions.isOwner && (app == null ? void 0 : app.forkedFromAppId) ? handleSyncUpstream : void 0,
7692
+ syncingUpstream,
7693
+ upstreamSyncStatus,
7569
7694
  onApprove: async (mr) => {
7570
7695
  if (processingMrId) return;
7571
7696
  setProcessingMrId(mr.id);