@calo-design/cli 0.12.2 → 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.
- package/bin/mirror-push.js +67 -6
- package/package.json +1 -1
package/bin/mirror-push.js
CHANGED
|
@@ -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
|
|
|
@@ -522,15 +523,23 @@ function channelForRef(ref: string): { channel: string; viaLauncher: boolean } {
|
|
|
522
523
|
// context created later (e.g. by a feed tap, which carries no URL) can't
|
|
523
524
|
// replay it as a phantom switch. Exception: a ref we bounce to the launcher
|
|
524
525
|
// stays in the store on purpose — the launcher consumes (and clears) it.
|
|
526
|
+
//
|
|
527
|
+
// NO slug-based self no-op: with version pinning, one slug spans MANY channels
|
|
528
|
+
// (head + every pinned version), and this same bundle serves several of them —
|
|
529
|
+
// so "slug === PROTOTYPE" cannot mean "already here". A plain self-link now
|
|
530
|
+
// switches to the HEAD channel (the escape hatch out of a pinned version; from
|
|
531
|
+
// the head it's a redundant refresh-to-latest, which is harmless and even
|
|
532
|
+
// useful). Ping-pong protection comes from the handle-once clearing + the
|
|
533
|
+
// switching latch, not from slug comparison.
|
|
525
534
|
function sweepLinkingURL(): void {
|
|
526
535
|
let url: string | null = null;
|
|
527
536
|
try { url = ExpoLinking.getLinkingURL() ?? null; } catch { return; }
|
|
528
537
|
if (!url) return;
|
|
529
538
|
const slug = launcherSlug(url);
|
|
530
539
|
if (slug === null) return; // the prototype's own deep link — not ours to touch
|
|
531
|
-
if (!slug
|
|
540
|
+
if (!slug) {
|
|
532
541
|
try { ExpoLinking.clearInitialURL(); } catch {}
|
|
533
|
-
return;
|
|
542
|
+
return; // bare /open — nothing to switch to
|
|
534
543
|
}
|
|
535
544
|
const target = channelForRef(slug);
|
|
536
545
|
if (!target.viaLauncher) {
|
|
@@ -539,6 +548,13 @@ function sweepLinkingURL(): void {
|
|
|
539
548
|
switchToChannel(target.channel);
|
|
540
549
|
}
|
|
541
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
|
+
|
|
542
558
|
export function subscribeLauncherLinks(): { remove: () => void } {
|
|
543
559
|
sweepLinkingURL(); // the URL that created this context (cold link or switch)
|
|
544
560
|
const linkSub = ExpoLinking.addEventListener("url", () => sweepLinkingURL());
|
|
@@ -596,13 +612,52 @@ function runtimeHasSentry() {
|
|
|
596
612
|
// killing the shell. Everything is defensive — on an old shell binary without
|
|
597
613
|
// the Sentry NATIVE module, init falls back to JS-only transport (and the
|
|
598
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
|
+
|
|
599
654
|
function mirrorChromeSource({ slug, owner }) {
|
|
600
655
|
if (!SENTRY_DSN || !runtimeHasSentry()) {
|
|
601
656
|
return `import { useEffect, type ReactNode } from "react";
|
|
657
|
+
import { View } from "react-native";
|
|
602
658
|
import { subscribeLauncherLinks } from "./mirror-switch";
|
|
659
|
+
import { MirrorEscape } from "./mirror-escape";
|
|
603
660
|
|
|
604
|
-
// Returning to the Mirror launcher is handled NATIVELY by the shell binary:
|
|
605
|
-
// shake the device (see calo-design-mirror/plugins/withCaloMirrorIos.js).
|
|
606
661
|
// Crash reporting was NOT injected: no Sentry DSN configured or the shared
|
|
607
662
|
// runtime predates @sentry/react-native (run \`calo-design update\`).
|
|
608
663
|
export function MirrorChrome({ children }: { children: ReactNode }) {
|
|
@@ -611,13 +666,19 @@ export function MirrorChrome({ children }: { children: ReactNode }) {
|
|
|
611
666
|
const sub = subscribeLauncherLinks();
|
|
612
667
|
return () => sub.remove();
|
|
613
668
|
}, []);
|
|
614
|
-
return
|
|
669
|
+
return (
|
|
670
|
+
<View style={{ flex: 1 }}>
|
|
671
|
+
{children}
|
|
672
|
+
<MirrorEscape />
|
|
673
|
+
</View>
|
|
674
|
+
);
|
|
615
675
|
}
|
|
616
676
|
`;
|
|
617
677
|
}
|
|
618
678
|
return `import { Component, type ErrorInfo, type ReactNode } from "react";
|
|
619
679
|
import { Pressable, ScrollView, Text, View } from "react-native";
|
|
620
680
|
import { subscribeLauncherLinks } from "./mirror-switch";
|
|
681
|
+
import { MirrorEscape } from "./mirror-escape";
|
|
621
682
|
|
|
622
683
|
// Injected by \`calo-design push\` — crash reporting for this prototype.
|
|
623
684
|
// Returning to the Mirror launcher stays NATIVE: shake the device.
|
|
@@ -662,7 +723,7 @@ export class MirrorChrome extends Component<{ children: ReactNode }, CrashState>
|
|
|
662
723
|
|
|
663
724
|
render() {
|
|
664
725
|
const { error } = this.state;
|
|
665
|
-
if (!error) return
|
|
726
|
+
if (!error) return <View style={{ flex: 1 }}>{this.props.children}<MirrorEscape /></View>;
|
|
666
727
|
return (
|
|
667
728
|
<View style={{ flex: 1, backgroundColor: "#ffffff", padding: 24, justifyContent: "center", gap: 12 }}>
|
|
668
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
|
+
"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"
|