@comergehq/studio 0.1.33 → 0.1.35

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.d.mts CHANGED
@@ -26,6 +26,7 @@ type BaseBundleMeta = {
26
26
  updatedAt: string;
27
27
  };
28
28
 
29
+ type ActiveAppChangedSource = 'initial' | 'fork_edit' | 'related_apps' | 'host_route_sync';
29
30
  type ComergeStudioProps = {
30
31
  appId: string;
31
32
  clientKey: string;
@@ -38,6 +39,11 @@ type ComergeStudioProps = {
38
39
  threadId?: string;
39
40
  source?: string;
40
41
  }) => void;
42
+ onActiveAppChanged?: (params: {
43
+ appId: string;
44
+ appKey?: string;
45
+ source: ActiveAppChangedSource;
46
+ }) => void;
41
47
  style?: ViewStyle;
42
48
  showBubble?: boolean;
43
49
  enableAgentProgress?: boolean;
@@ -45,7 +51,7 @@ type ComergeStudioProps = {
45
51
  embeddedBaseBundles?: EmbeddedBaseBundles;
46
52
  onSystemEvent?: (event: ComergeRuntimeSystemEventEnvelope) => void;
47
53
  };
48
- declare function ComergeStudio({ appId, clientKey, appKey, analyticsEnabled, onNavigateHome, onOpenAppRequested, style, showBubble, enableAgentProgress, studioControlOptions, embeddedBaseBundles, onSystemEvent, }: ComergeStudioProps): react_jsx_runtime.JSX.Element;
54
+ declare function ComergeStudio({ appId, clientKey, appKey, analyticsEnabled, onNavigateHome, onOpenAppRequested, onActiveAppChanged, style, showBubble, enableAgentProgress, studioControlOptions, embeddedBaseBundles, onSystemEvent, }: ComergeStudioProps): react_jsx_runtime.JSX.Element;
49
55
 
50
56
  declare function setSupabaseClient(client: SupabaseClient): void;
51
57
 
package/dist/index.d.ts CHANGED
@@ -26,6 +26,7 @@ type BaseBundleMeta = {
26
26
  updatedAt: string;
27
27
  };
28
28
 
29
+ type ActiveAppChangedSource = 'initial' | 'fork_edit' | 'related_apps' | 'host_route_sync';
29
30
  type ComergeStudioProps = {
30
31
  appId: string;
31
32
  clientKey: string;
@@ -38,6 +39,11 @@ type ComergeStudioProps = {
38
39
  threadId?: string;
39
40
  source?: string;
40
41
  }) => void;
42
+ onActiveAppChanged?: (params: {
43
+ appId: string;
44
+ appKey?: string;
45
+ source: ActiveAppChangedSource;
46
+ }) => void;
41
47
  style?: ViewStyle;
42
48
  showBubble?: boolean;
43
49
  enableAgentProgress?: boolean;
@@ -45,7 +51,7 @@ type ComergeStudioProps = {
45
51
  embeddedBaseBundles?: EmbeddedBaseBundles;
46
52
  onSystemEvent?: (event: ComergeRuntimeSystemEventEnvelope) => void;
47
53
  };
48
- declare function ComergeStudio({ appId, clientKey, appKey, analyticsEnabled, onNavigateHome, onOpenAppRequested, style, showBubble, enableAgentProgress, studioControlOptions, embeddedBaseBundles, onSystemEvent, }: ComergeStudioProps): react_jsx_runtime.JSX.Element;
54
+ declare function ComergeStudio({ appId, clientKey, appKey, analyticsEnabled, onNavigateHome, onOpenAppRequested, onActiveAppChanged, style, showBubble, enableAgentProgress, studioControlOptions, embeddedBaseBundles, onSystemEvent, }: ComergeStudioProps): react_jsx_runtime.JSX.Element;
49
55
 
50
56
  declare function setSupabaseClient(client: SupabaseClient): void;
51
57
 
package/dist/index.js CHANGED
@@ -8361,7 +8361,26 @@ ${trimmedLine2}\u2026 `;
8361
8361
 
8362
8362
  // src/components/chat/AgentProgressCard.tsx
8363
8363
  var import_react_native56 = require("react-native");
8364
+
8365
+ // src/components/icons/RemixXLoopLottie.tsx
8366
+ var import_lottie_react_native = __toESM(require("lottie-react-native"));
8364
8367
  var import_jsx_runtime57 = require("react/jsx-runtime");
8368
+ var remixXLoopSource = require_remix_x_loop_lottie();
8369
+ var Lottie = import_lottie_react_native.default;
8370
+ function RemixXLoopLottie({ size = 24, style }) {
8371
+ return /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(
8372
+ Lottie,
8373
+ {
8374
+ source: remixXLoopSource,
8375
+ autoPlay: true,
8376
+ loop: true,
8377
+ style: [{ width: size, height: size }, style]
8378
+ }
8379
+ );
8380
+ }
8381
+
8382
+ // src/components/chat/AgentProgressCard.tsx
8383
+ var import_jsx_runtime58 = require("react/jsx-runtime");
8365
8384
  function titleForPhase(phase) {
8366
8385
  if (phase === "planning") return "Planning";
8367
8386
  if (phase === "reasoning") return "Reasoning";
@@ -8383,9 +8402,10 @@ function AgentProgressCard({ progress }) {
8383
8402
  const theme = useTheme();
8384
8403
  const statusLabel = titleForStatus(progress.status);
8385
8404
  const phaseLabel = titleForPhase(progress.phase);
8405
+ const showAnimatedStatusIcon = progress.status === "running";
8386
8406
  const subtitle = progress.latestMessage || `Agent is ${phaseLabel.toLowerCase()}...`;
8387
8407
  const todo = progress.todoSummary;
8388
- return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
8408
+ return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
8389
8409
  import_react_native56.View,
8390
8410
  {
8391
8411
  style: {
@@ -8397,20 +8417,23 @@ function AgentProgressCard({ progress }) {
8397
8417
  backgroundColor: withAlpha(theme.colors.surface, theme.scheme === "dark" ? 0.84 : 0.94)
8398
8418
  },
8399
8419
  children: [
8400
- /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_react_native56.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginBottom: 4 }, children: [
8401
- /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(Text, { variant: "caption", children: statusLabel }),
8402
- /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(Text, { variant: "captionMuted", children: phaseLabel })
8420
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_react_native56.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginBottom: 4 }, children: [
8421
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Text, { variant: "caption", children: statusLabel }),
8422
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_react_native56.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
8423
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Text, { variant: "captionMuted", children: phaseLabel }),
8424
+ showAnimatedStatusIcon ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(RemixXLoopLottie, { size: 20, style: { marginLeft: 8 } }) : null
8425
+ ] })
8403
8426
  ] }),
8404
- /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(Text, { variant: "bodyMuted", children: subtitle }),
8405
- progress.changedFilesCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(Text, { variant: "captionMuted", style: { marginTop: 8 }, children: [
8427
+ /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Text, { variant: "bodyMuted", children: subtitle }),
8428
+ progress.changedFilesCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Text, { variant: "captionMuted", style: { marginTop: 8 }, children: [
8406
8429
  "Updated files: ",
8407
8430
  progress.changedFilesCount
8408
8431
  ] }) : null,
8409
- progress.recentFiles.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(import_react_native56.View, { style: { marginTop: 6 }, children: progress.recentFiles.map((path) => /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(Text, { variant: "captionMuted", numberOfLines: 1, children: [
8432
+ progress.recentFiles.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(import_react_native56.View, { style: { marginTop: 6 }, children: progress.recentFiles.map((path) => /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Text, { variant: "captionMuted", numberOfLines: 1, children: [
8410
8433
  "\u2022 ",
8411
8434
  path
8412
8435
  ] }, path)) }) : null,
8413
- todo ? /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(Text, { variant: "captionMuted", style: { marginTop: 8 }, children: [
8436
+ todo ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Text, { variant: "captionMuted", style: { marginTop: 8 }, children: [
8414
8437
  "Todos: ",
8415
8438
  todo.completed,
8416
8439
  "/",
@@ -8425,7 +8448,7 @@ function AgentProgressCard({ progress }) {
8425
8448
 
8426
8449
  // src/components/chat/BundleProgressCard.tsx
8427
8450
  var import_react_native57 = require("react-native");
8428
- var import_jsx_runtime58 = require("react/jsx-runtime");
8451
+ var import_jsx_runtime59 = require("react/jsx-runtime");
8429
8452
  function titleForStatus2(status) {
8430
8453
  if (status === "succeeded") return "Completed";
8431
8454
  if (status === "failed") return "Failed";
@@ -8437,7 +8460,7 @@ function BundleProgressCard({ progress }) {
8437
8460
  const percent = Math.round(Math.max(0, Math.min(1, progress.progressValue)) * 100);
8438
8461
  const fillColor = progress.status === "failed" ? theme.colors.danger : progress.status === "succeeded" ? theme.colors.success : theme.colors.warning;
8439
8462
  const detail = progress.errorMessage || progress.phaseLabel;
8440
- return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(
8463
+ return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(
8441
8464
  import_react_native57.View,
8442
8465
  {
8443
8466
  accessible: true,
@@ -8453,14 +8476,14 @@ function BundleProgressCard({ progress }) {
8453
8476
  backgroundColor: withAlpha(theme.colors.surface, theme.scheme === "dark" ? 0.84 : 0.94)
8454
8477
  },
8455
8478
  children: [
8456
- /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(import_react_native57.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }, children: [
8457
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Text, { variant: "caption", children: statusLabel }),
8458
- /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Text, { variant: "captionMuted", children: [
8479
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_react_native57.View, { style: { flexDirection: "row", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }, children: [
8480
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Text, { variant: "caption", children: statusLabel }),
8481
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Text, { variant: "captionMuted", children: [
8459
8482
  percent,
8460
8483
  "%"
8461
8484
  ] })
8462
8485
  ] }),
8463
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
8486
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8464
8487
  import_react_native57.View,
8465
8488
  {
8466
8489
  style: {
@@ -8470,7 +8493,7 @@ function BundleProgressCard({ progress }) {
8470
8493
  backgroundColor: withAlpha(theme.colors.border, theme.scheme === "dark" ? 0.5 : 0.6),
8471
8494
  overflow: "hidden"
8472
8495
  },
8473
- children: /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(
8496
+ children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8474
8497
  import_react_native57.View,
8475
8498
  {
8476
8499
  style: {
@@ -8482,14 +8505,14 @@ function BundleProgressCard({ progress }) {
8482
8505
  )
8483
8506
  }
8484
8507
  ),
8485
- /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Text, { variant: "captionMuted", numberOfLines: 1, style: { marginTop: 8, minHeight: 16 }, children: detail })
8508
+ /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Text, { variant: "captionMuted", numberOfLines: 1, style: { marginTop: 8, minHeight: 16 }, children: detail })
8486
8509
  ]
8487
8510
  }
8488
8511
  );
8489
8512
  }
8490
8513
 
8491
8514
  // src/studio/ui/ChatPanel.tsx
8492
- var import_jsx_runtime59 = require("react/jsx-runtime");
8515
+ var import_jsx_runtime60 = require("react/jsx-runtime");
8493
8516
  function ChatPanel({
8494
8517
  title = "Chat",
8495
8518
  messages,
@@ -8534,21 +8557,21 @@ function ChatPanel({
8534
8557
  var _a;
8535
8558
  (_a = listRef.current) == null ? void 0 : _a.scrollToBottom({ animated: true });
8536
8559
  }, []);
8537
- const header = /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8560
+ const header = /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
8538
8561
  ChatHeader,
8539
8562
  {
8540
- left: /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_react_native58.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
8541
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(StudioSheetHeaderIconButton, { onPress: onBack, accessibilityLabel: "Back", style: { marginRight: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(IconBack, { size: 20, colorToken: "floatingContent" }) }),
8542
- onNavigateHome ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(StudioSheetHeaderIconButton, { onPress: onNavigateHome, accessibilityLabel: "Home", children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(IconHome, { size: 20, colorToken: "floatingContent" }) }) : null
8563
+ left: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_react_native58.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
8564
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(StudioSheetHeaderIconButton, { onPress: onBack, accessibilityLabel: "Back", style: { marginRight: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(IconBack, { size: 20, colorToken: "floatingContent" }) }),
8565
+ onNavigateHome ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(StudioSheetHeaderIconButton, { onPress: onNavigateHome, accessibilityLabel: "Home", children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(IconHome, { size: 20, colorToken: "floatingContent" }) }) : null
8543
8566
  ] }),
8544
- right: /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_react_native58.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
8545
- onStartDraw ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(StudioSheetHeaderIconButton, { onPress: onStartDraw, accessibilityLabel: "Draw", intent: "danger", style: { marginRight: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(IconDraw, { size: 20, colorToken: "onDanger" }) }) : null,
8546
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(IconClose, { size: 20, colorToken: "floatingContent" }) })
8567
+ right: /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_react_native58.View, { style: { flexDirection: "row", alignItems: "center" }, children: [
8568
+ onStartDraw ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(StudioSheetHeaderIconButton, { onPress: onStartDraw, accessibilityLabel: "Draw", intent: "danger", style: { marginRight: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(IconDraw, { size: 20, colorToken: "onDanger" }) }) : null,
8569
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(StudioSheetHeaderIconButton, { onPress: onClose, accessibilityLabel: "Close", children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(IconClose, { size: 20, colorToken: "floatingContent" }) })
8547
8570
  ] }),
8548
8571
  center: null
8549
8572
  }
8550
8573
  );
8551
- const topBanner = shouldForkOnEdit ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8574
+ const topBanner = shouldForkOnEdit ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
8552
8575
  ForkNoticeBanner,
8553
8576
  {
8554
8577
  isOwner: !shouldForkOnEdit,
@@ -8557,22 +8580,22 @@ function ChatPanel({
8557
8580
  ) : null;
8558
8581
  const showMessagesLoading = Boolean(loading) && messages.length === 0;
8559
8582
  if (showMessagesLoading) {
8560
- return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_react_native58.View, { style: { flex: 1 }, children: [
8561
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_react_native58.View, { children: header }),
8562
- topBanner ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_react_native58.View, { style: { paddingHorizontal: 16, paddingTop: 8 }, children: topBanner }) : null,
8563
- /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_react_native58.View, { style: { flex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 24, paddingVertical: 12 }, children: [
8564
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_react_native58.ActivityIndicator, {}),
8565
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(import_react_native58.View, { style: { height: 12 } }),
8566
- /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Text, { variant: "bodyMuted", children: "Loading messages\u2026" })
8583
+ return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_react_native58.View, { style: { flex: 1 }, children: [
8584
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_react_native58.View, { children: header }),
8585
+ topBanner ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_react_native58.View, { style: { paddingHorizontal: 16, paddingTop: 8 }, children: topBanner }) : null,
8586
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_react_native58.View, { style: { flex: 1, alignItems: "center", justifyContent: "center", paddingHorizontal: 24, paddingVertical: 12 }, children: [
8587
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_react_native58.ActivityIndicator, {}),
8588
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_react_native58.View, { style: { height: 12 } }),
8589
+ /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Text, { variant: "bodyMuted", children: "Loading messages\u2026" })
8567
8590
  ] })
8568
8591
  ] });
8569
8592
  }
8570
8593
  const bundleProgress = (progress == null ? void 0 : progress.bundle) ?? null;
8571
- const queueTop = progress || queueItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(import_react_native58.View, { style: { gap: theme.spacing.sm }, children: [
8572
- progress ? bundleProgress ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(BundleProgressCard, { progress: bundleProgress }) : /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(AgentProgressCard, { progress }) : null,
8573
- !progress && queueItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(ChatQueue, { items: queueItems, onRemove: onRemoveQueueItem }) : null
8594
+ const queueTop = progress || queueItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_react_native58.View, { style: { gap: theme.spacing.sm }, children: [
8595
+ progress ? bundleProgress ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(BundleProgressCard, { progress: bundleProgress }) : /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(AgentProgressCard, { progress }) : null,
8596
+ !progress && queueItems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ChatQueue, { items: queueItems, onRemove: onRemoveQueueItem }) : null
8574
8597
  ] }) : null;
8575
- return /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8598
+ return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
8576
8599
  ChatPage,
8577
8600
  {
8578
8601
  header,
@@ -8585,13 +8608,13 @@ function ChatPanel({
8585
8608
  composerHorizontalPadding: 0,
8586
8609
  listRef,
8587
8610
  onNearBottomChange: setNearBottom,
8588
- overlay: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(
8611
+ overlay: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
8589
8612
  ScrollToBottomButton,
8590
8613
  {
8591
8614
  visible: !nearBottom,
8592
8615
  onPress: handleScrollToBottom,
8593
8616
  style: { bottom: 80 },
8594
- children: /* @__PURE__ */ (0, import_jsx_runtime59.jsx)(IconArrowDown, { size: 20, colorToken: "floatingContent" })
8617
+ children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(IconArrowDown, { size: 20, colorToken: "floatingContent" })
8595
8618
  }
8596
8619
  ),
8597
8620
  composer: {
@@ -8613,7 +8636,7 @@ function ChatPanel({
8613
8636
  // src/components/dialogs/ConfirmMergeRequestDialog.tsx
8614
8637
  var React44 = __toESM(require("react"));
8615
8638
  var import_react_native59 = require("react-native");
8616
- var import_jsx_runtime60 = require("react/jsx-runtime");
8639
+ var import_jsx_runtime61 = require("react/jsx-runtime");
8617
8640
  function ConfirmMergeRequestDialog({
8618
8641
  visible,
8619
8642
  onOpenChange,
@@ -8643,7 +8666,7 @@ function ConfirmMergeRequestDialog({
8643
8666
  justifyContent: "center",
8644
8667
  alignSelf: "stretch"
8645
8668
  };
8646
- return /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(
8669
+ return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(
8647
8670
  Modal,
8648
8671
  {
8649
8672
  visible,
@@ -8654,7 +8677,7 @@ function ConfirmMergeRequestDialog({
8654
8677
  backgroundColor: theme.colors.background
8655
8678
  },
8656
8679
  children: [
8657
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_react_native59.View, { children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
8680
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(import_react_native59.View, { children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
8658
8681
  Text,
8659
8682
  {
8660
8683
  style: {
@@ -8666,8 +8689,8 @@ function ConfirmMergeRequestDialog({
8666
8689
  children: "Are you sure you want to approve this merge request?"
8667
8690
  }
8668
8691
  ) }),
8669
- /* @__PURE__ */ (0, import_jsx_runtime60.jsxs)(import_react_native59.View, { style: { marginTop: 16 }, children: [
8670
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
8692
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(import_react_native59.View, { style: { marginTop: 16 }, children: [
8693
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
8671
8694
  import_react_native59.View,
8672
8695
  {
8673
8696
  style: [
@@ -8677,7 +8700,7 @@ function ConfirmMergeRequestDialog({
8677
8700
  opacity: canConfirm ? 1 : 0.5
8678
8701
  }
8679
8702
  ],
8680
- children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
8703
+ children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
8681
8704
  import_react_native59.Pressable,
8682
8705
  {
8683
8706
  accessibilityRole: "button",
@@ -8685,13 +8708,13 @@ function ConfirmMergeRequestDialog({
8685
8708
  disabled: !canConfirm,
8686
8709
  onPress: handleConfirm,
8687
8710
  style: [fullWidthButtonBase, { flex: 1 }],
8688
- children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Text, { style: { textAlign: "center", color: theme.colors.onPrimary }, children: "Approve Merge" })
8711
+ children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Text, { style: { textAlign: "center", color: theme.colors.onPrimary }, children: "Approve Merge" })
8689
8712
  }
8690
8713
  )
8691
8714
  }
8692
8715
  ),
8693
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_react_native59.View, { style: { height: 8 } }),
8694
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
8716
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(import_react_native59.View, { style: { height: 8 } }),
8717
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
8695
8718
  import_react_native59.View,
8696
8719
  {
8697
8720
  style: [
@@ -8703,7 +8726,7 @@ function ConfirmMergeRequestDialog({
8703
8726
  opacity: isBuilding || !mergeRequest ? 0.5 : 1
8704
8727
  }
8705
8728
  ],
8706
- children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
8729
+ children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
8707
8730
  import_react_native59.Pressable,
8708
8731
  {
8709
8732
  accessibilityRole: "button",
@@ -8711,13 +8734,13 @@ function ConfirmMergeRequestDialog({
8711
8734
  disabled: isBuilding || !mergeRequest,
8712
8735
  onPress: handleTestFirst,
8713
8736
  style: [fullWidthButtonBase, { flex: 1 }],
8714
- children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Text, { style: { textAlign: "center", color: theme.colors.text }, children: isBuilding ? "Preparing\u2026" : "Test edits first" })
8737
+ children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Text, { style: { textAlign: "center", color: theme.colors.text }, children: isBuilding ? "Preparing\u2026" : "Test edits first" })
8715
8738
  }
8716
8739
  )
8717
8740
  }
8718
8741
  ),
8719
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(import_react_native59.View, { style: { height: 8 } }),
8720
- /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
8742
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(import_react_native59.View, { style: { height: 8 } }),
8743
+ /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
8721
8744
  import_react_native59.View,
8722
8745
  {
8723
8746
  style: [
@@ -8728,14 +8751,14 @@ function ConfirmMergeRequestDialog({
8728
8751
  borderColor: theme.colors.border
8729
8752
  }
8730
8753
  ],
8731
- children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
8754
+ children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
8732
8755
  import_react_native59.Pressable,
8733
8756
  {
8734
8757
  accessibilityRole: "button",
8735
8758
  accessibilityLabel: "Cancel",
8736
8759
  onPress: close,
8737
8760
  style: [fullWidthButtonBase, { flex: 1 }],
8738
- children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Text, { style: { textAlign: "center", color: theme.colors.text }, children: "Cancel" })
8761
+ children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Text, { style: { textAlign: "center", color: theme.colors.text }, children: "Cancel" })
8739
8762
  }
8740
8763
  )
8741
8764
  }
@@ -8747,7 +8770,7 @@ function ConfirmMergeRequestDialog({
8747
8770
  }
8748
8771
 
8749
8772
  // src/studio/ui/ConfirmMergeFlow.tsx
8750
- var import_jsx_runtime61 = require("react/jsx-runtime");
8773
+ var import_jsx_runtime62 = require("react/jsx-runtime");
8751
8774
  function ConfirmMergeFlow({
8752
8775
  visible,
8753
8776
  onOpenChange,
@@ -8758,7 +8781,7 @@ function ConfirmMergeFlow({
8758
8781
  onConfirm,
8759
8782
  onTestFirst
8760
8783
  }) {
8761
- return /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
8784
+ return /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
8762
8785
  ConfirmMergeRequestDialog,
8763
8786
  {
8764
8787
  visible,
@@ -8913,24 +8936,24 @@ var import_studio_control = require("@comergehq/studio-control");
8913
8936
 
8914
8937
  // src/components/icons/RemixUpIcon.tsx
8915
8938
  var import_react_native_svg3 = __toESM(require("react-native-svg"));
8916
- var import_jsx_runtime62 = require("react/jsx-runtime");
8939
+ var import_jsx_runtime63 = require("react/jsx-runtime");
8917
8940
  function RemixUpIcon({ width = 24, height = 24, ...props }) {
8918
- return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(import_react_native_svg3.default, { viewBox: "0 0 70 49", width, height, fill: "none", ...props, children: [
8919
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
8941
+ return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(import_react_native_svg3.default, { viewBox: "0 0 70 49", width, height, fill: "none", ...props, children: [
8942
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
8920
8943
  import_react_native_svg3.Path,
8921
8944
  {
8922
8945
  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
8946
  fill: "#00CBC0"
8924
8947
  }
8925
8948
  ),
8926
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
8949
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
8927
8950
  import_react_native_svg3.Path,
8928
8951
  {
8929
8952
  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
8953
  fill: "#FF1820"
8931
8954
  }
8932
8955
  ),
8933
- /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
8956
+ /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
8934
8957
  import_react_native_svg3.Path,
8935
8958
  {
8936
8959
  d: "M34.7656 2.28882e-05L21.44 13.2661L34.706 26.5321L47.972 13.266L34.7656 2.28882e-05Z",
@@ -8940,23 +8963,6 @@ function RemixUpIcon({ width = 24, height = 24, ...props }) {
8940
8963
  ] });
8941
8964
  }
8942
8965
 
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
8966
  // src/studio/ui/StudioOverlay.tsx
8961
8967
  var import_jsx_runtime64 = require("react/jsx-runtime");
8962
8968
  function StudioOverlay({
@@ -9891,6 +9897,7 @@ function ComergeStudio({
9891
9897
  analyticsEnabled,
9892
9898
  onNavigateHome,
9893
9899
  onOpenAppRequested,
9900
+ onActiveAppChanged,
9894
9901
  style,
9895
9902
  showBubble = true,
9896
9903
  enableAgentProgress = true,
@@ -9901,12 +9908,36 @@ function ComergeStudio({
9901
9908
  const [activeAppId, setActiveAppId] = React51.useState(appId);
9902
9909
  const [runtimeAppId, setRuntimeAppId] = React51.useState(appId);
9903
9910
  const [pendingRuntimeTargetAppId, setPendingRuntimeTargetAppId] = React51.useState(null);
9911
+ const didSyncFromHostRef = React51.useRef(false);
9912
+ const lastNotifiedRef = React51.useRef(null);
9904
9913
  const platform = React51.useMemo(() => import_react_native61.Platform.OS === "ios" ? "ios" : "android", []);
9914
+ const notifyActiveAppChanged = React51.useCallback(
9915
+ (nextAppId, source) => {
9916
+ if (!onActiveAppChanged) return;
9917
+ const trimmedAppId = nextAppId.trim();
9918
+ if (!trimmedAppId) return;
9919
+ const nextAppKey = (appKey == null ? void 0 : appKey.trim()) || "MicroMain";
9920
+ const dedupeKey = `${trimmedAppId}:${nextAppKey}`;
9921
+ if (lastNotifiedRef.current === dedupeKey) return;
9922
+ lastNotifiedRef.current = dedupeKey;
9923
+ onActiveAppChanged({ appId: trimmedAppId, appKey: nextAppKey, source });
9924
+ },
9925
+ [appKey, onActiveAppChanged]
9926
+ );
9927
+ const setActiveAppIdWithSource = React51.useCallback(
9928
+ (nextAppId, source) => {
9929
+ setActiveAppId(nextAppId);
9930
+ notifyActiveAppChanged(nextAppId, source);
9931
+ },
9932
+ [notifyActiveAppChanged]
9933
+ );
9905
9934
  React51.useEffect(() => {
9906
- setActiveAppId(appId);
9935
+ const source = didSyncFromHostRef.current ? "host_route_sync" : "initial";
9936
+ didSyncFromHostRef.current = true;
9937
+ setActiveAppIdWithSource(appId, source);
9907
9938
  setRuntimeAppId(appId);
9908
9939
  setPendingRuntimeTargetAppId(null);
9909
- }, [appId]);
9940
+ }, [appId, setActiveAppIdWithSource]);
9910
9941
  const captureTargetRef = React51.useRef(null);
9911
9942
  return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
9912
9943
  StudioBootstrap,
@@ -9919,7 +9950,7 @@ function ComergeStudio({
9919
9950
  {
9920
9951
  userId,
9921
9952
  activeAppId,
9922
- setActiveAppId,
9953
+ setActiveAppId: setActiveAppIdWithSource,
9923
9954
  runtimeAppId,
9924
9955
  setRuntimeAppId,
9925
9956
  pendingRuntimeTargetAppId,
@@ -10039,7 +10070,7 @@ function ComergeStudioInner({
10039
10070
  userId,
10040
10071
  app,
10041
10072
  onForkedApp: (id, opts) => {
10042
- setActiveAppId(id);
10073
+ setActiveAppId(id, "fork_edit");
10043
10074
  const keepRenderingAppId = opts == null ? void 0 : opts.keepRenderingAppId;
10044
10075
  if (keepRenderingAppId) {
10045
10076
  setRuntimeAppId(keepRenderingAppId);
@@ -10184,7 +10215,7 @@ function ComergeStudioInner({
10184
10215
  source: "related_apps_switcher"
10185
10216
  });
10186
10217
  } else {
10187
- setActiveAppId(targetAppId);
10218
+ setActiveAppId(targetAppId, "related_apps");
10188
10219
  setRuntimeAppId(targetAppId);
10189
10220
  setPendingRuntimeTargetAppId(null);
10190
10221
  }