@calo-design/cli 0.12.3 → 0.12.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 (2) hide show
  1. package/bin/mirror-push.js +57 -4
  2. package/package.json +1 -1
@@ -404,6 +404,7 @@ export default function RootLayout() {
404
404
  }
405
405
  fs.writeFileSync(path.join(stagedAppDir, "_layout.tsx"), managed);
406
406
  fs.writeFileSync(path.join(mirrorChromeDir, "mirror-chrome.tsx"), mirrorChromeSource({ slug, owner }));
407
+ fs.writeFileSync(path.join(mirrorChromeDir, "mirror-escape.tsx"), mirrorEscapeSource());
407
408
  injectNativeIntent(stagedAppDir, mirrorChromeDir, slug);
408
409
  }
409
410
 
@@ -547,6 +548,13 @@ function sweepLinkingURL(): void {
547
548
  switchToChannel(target.channel);
548
549
  }
549
550
 
551
+ // Return to the Mirror launcher (its channel is "mirror"). The shell's native
552
+ // shake-to-return exists too, but this JS path works on every binary — it's what
553
+ // the chrome's floating escape control calls.
554
+ export function returnToMirror(): void {
555
+ switchToChannel("mirror");
556
+ }
557
+
550
558
  export function subscribeLauncherLinks(): { remove: () => void } {
551
559
  sweepLinkingURL(); // the URL that created this context (cold link or switch)
552
560
  const linkSub = ExpoLinking.addEventListener("url", () => sweepLinkingURL());
@@ -604,13 +612,52 @@ function runtimeHasSentry() {
604
612
  // killing the shell. Everything is defensive — on an old shell binary without
605
613
  // the Sentry NATIVE module, init falls back to JS-only transport (and the
606
614
  // try/catch backstops that), so prototypes never crash BECAUSE of reporting.
615
+ // Floating return-to-launcher control, rendered by BOTH MirrorChrome variants.
616
+ // The shell's native shake-to-return exists too, but this JS escape works on
617
+ // every binary and every OS — a prototype must never be a dead end. Small and
618
+ // translucent on purpose; bottom-left so it never collides with FABs/CTAs that
619
+ // conventionally live bottom-right.
620
+ function mirrorEscapeSource() {
621
+ return `import { Pressable, Text } from "react-native";
622
+ import { returnToMirror } from "./mirror-switch";
623
+
624
+ // Managed by calo-design push — the always-available way OUT of a prototype.
625
+ export function MirrorEscape() {
626
+ return (
627
+ <Pressable
628
+ onPress={returnToMirror}
629
+ accessibilityRole="button"
630
+ accessibilityLabel="Back to Calo Mirror"
631
+ hitSlop={10}
632
+ style={({ pressed }) => ({
633
+ position: "absolute",
634
+ left: 12,
635
+ bottom: 44,
636
+ width: 38,
637
+ height: 38,
638
+ borderRadius: 19,
639
+ alignItems: "center",
640
+ justifyContent: "center",
641
+ backgroundColor: "rgba(17,17,17,0.42)",
642
+ opacity: pressed ? 0.9 : 0.55,
643
+ zIndex: 9999,
644
+ elevation: 9,
645
+ })}
646
+ >
647
+ <Text style={{ color: "#ffffff", fontSize: 17, lineHeight: 20 }}>{"\\u21A9"}</Text>
648
+ </Pressable>
649
+ );
650
+ }
651
+ `;
652
+ }
653
+
607
654
  function mirrorChromeSource({ slug, owner }) {
608
655
  if (!SENTRY_DSN || !runtimeHasSentry()) {
609
656
  return `import { useEffect, type ReactNode } from "react";
657
+ import { View } from "react-native";
610
658
  import { subscribeLauncherLinks } from "./mirror-switch";
659
+ import { MirrorEscape } from "./mirror-escape";
611
660
 
612
- // Returning to the Mirror launcher is handled NATIVELY by the shell binary:
613
- // shake the device (see calo-design-mirror/plugins/withCaloMirrorIos.js).
614
661
  // Crash reporting was NOT injected: no Sentry DSN configured or the shared
615
662
  // runtime predates @sentry/react-native (run \`calo-design update\`).
616
663
  export function MirrorChrome({ children }: { children: ReactNode }) {
@@ -619,13 +666,19 @@ export function MirrorChrome({ children }: { children: ReactNode }) {
619
666
  const sub = subscribeLauncherLinks();
620
667
  return () => sub.remove();
621
668
  }, []);
622
- return <>{children}</>;
669
+ return (
670
+ <View style={{ flex: 1 }}>
671
+ {children}
672
+ <MirrorEscape />
673
+ </View>
674
+ );
623
675
  }
624
676
  `;
625
677
  }
626
678
  return `import { Component, type ErrorInfo, type ReactNode } from "react";
627
679
  import { Pressable, ScrollView, Text, View } from "react-native";
628
680
  import { subscribeLauncherLinks } from "./mirror-switch";
681
+ import { MirrorEscape } from "./mirror-escape";
629
682
 
630
683
  // Injected by \`calo-design push\` — crash reporting for this prototype.
631
684
  // Returning to the Mirror launcher stays NATIVE: shake the device.
@@ -670,7 +723,7 @@ export class MirrorChrome extends Component<{ children: ReactNode }, CrashState>
670
723
 
671
724
  render() {
672
725
  const { error } = this.state;
673
- if (!error) return <>{this.props.children}</>;
726
+ if (!error) return <View style={{ flex: 1 }}>{this.props.children}<MirrorEscape /></View>;
674
727
  return (
675
728
  <View style={{ flex: 1, backgroundColor: "#ffffff", padding: 24, justifyContent: "center", gap: 12 }}>
676
729
  <Text style={{ fontSize: 20, fontWeight: "700", color: "#1a1a1a" }}>This prototype crashed</Text>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@calo-design/cli",
3
- "version": "0.12.3",
3
+ "version": "0.12.4",
4
4
  "description": "One-line setup for Calo design tooling: logs in with your Calo email and installs the calo-design skill + design-system packages. No GitHub account needed.",
5
5
  "bin": {
6
6
  "calo-design": "bin/cli.js"