@omg-dev/sdk 0.4.25 → 0.4.27
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/{OmgBadge-LAcimQp0.mjs → OmgBadge-CDnTIlnn.mjs} +28 -2
- package/dist/brand/auto.mjs +1 -1
- package/dist/index.mjs +51 -22
- package/package.json +1 -1
- package/src/auth/client.ts +42 -0
- package/src/auth/react.tsx +26 -29
- package/src/brand/OmgBadge.tsx +46 -9
- package/src/brand/auto.tsx +5 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as VibesFeedback } from "./VibesFeedback-BF2Vf6FK.mjs";
|
|
2
|
-
import { useId, useState } from "react";
|
|
2
|
+
import { useEffect, useId, useState } from "react";
|
|
3
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
//#region src/brand/OmgBadge.tsx
|
|
5
5
|
const BRAND = "#FF5530";
|
|
@@ -15,6 +15,7 @@ function feedbackBundled() {
|
|
|
15
15
|
}
|
|
16
16
|
const DISMISS_KEY = "vibes-remix:dismissed";
|
|
17
17
|
const REMIX_ORIGIN = typeof import.meta !== "undefined" && import.meta.env?.VITE_REMIX_ORIGIN || "https://omg.dev";
|
|
18
|
+
const CONTROLPLANE_URL = (typeof import.meta !== "undefined" && import.meta.env?.VITE_CONTROLPLANE_URL || "https://backend.omg.dev").replace(/\/$/, "");
|
|
18
19
|
function appSlug() {
|
|
19
20
|
if (typeof import.meta === "undefined") return null;
|
|
20
21
|
const slug = import.meta.env?.VITE_APP_SLUG?.trim();
|
|
@@ -33,13 +34,38 @@ function setDismissedFlag() {
|
|
|
33
34
|
localStorage.setItem(DISMISS_KEY, "1");
|
|
34
35
|
} catch {}
|
|
35
36
|
}
|
|
37
|
+
/** Live check: only Explore-listed public apps may show "Make it mine". Fail closed. */
|
|
38
|
+
async function fetchRemixable(slug) {
|
|
39
|
+
try {
|
|
40
|
+
const res = await fetch(`${CONTROLPLANE_URL}/api/projects/isRemixable`, {
|
|
41
|
+
method: "POST",
|
|
42
|
+
headers: { "content-type": "application/json" },
|
|
43
|
+
body: JSON.stringify({ slug }),
|
|
44
|
+
cache: "no-store"
|
|
45
|
+
});
|
|
46
|
+
if (!res.ok) return false;
|
|
47
|
+
return (await res.json())?.remixable === true;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
36
52
|
function OmgBadge({ href = "https://omg.dev", label = "What is omg?" }) {
|
|
37
53
|
const [open, setOpen] = useState(false);
|
|
38
54
|
const slug = appSlug();
|
|
39
|
-
const [remix, setRemix] = useState(
|
|
55
|
+
const [remix, setRemix] = useState(false);
|
|
40
56
|
const titleId = useId();
|
|
41
57
|
const descriptionId = useId();
|
|
42
58
|
const markId = useId().replace(/[^a-zA-Z0-9_-]/g, "");
|
|
59
|
+
useEffect(() => {
|
|
60
|
+
if (!slug || isDismissed()) return;
|
|
61
|
+
let cancelled = false;
|
|
62
|
+
fetchRemixable(slug).then((ok) => {
|
|
63
|
+
if (!cancelled && ok && !isDismissed()) setRemix(true);
|
|
64
|
+
});
|
|
65
|
+
return () => {
|
|
66
|
+
cancelled = true;
|
|
67
|
+
};
|
|
68
|
+
}, [slug]);
|
|
43
69
|
function dismissRemix() {
|
|
44
70
|
setDismissedFlag();
|
|
45
71
|
setRemix(false);
|
package/dist/brand/auto.mjs
CHANGED
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as installTrace, c as requestMotionPermission, d as getAuthContext, f as notifyAuthRequired, h as subscribeAuthRequired, i as getTrace, l as useFeedbackGesture, m as subscribeAuthChange, n as captureScreenshot, o as attachGestureListeners, p as setAuthContext, r as clearTrace, s as motionPermissionState, t as VibesFeedback, u as useUpload } from "./VibesFeedback-BF2Vf6FK.mjs";
|
|
2
|
-
import { t as OmgBadge } from "./OmgBadge-
|
|
2
|
+
import { t as OmgBadge } from "./OmgBadge-CDnTIlnn.mjs";
|
|
3
3
|
import { createContext, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
4
|
import { fetchEventSource } from "@microsoft/fetch-event-source";
|
|
5
5
|
import { createAuthClient } from "better-auth/react";
|
|
@@ -7,6 +7,37 @@ import { passkeyClient } from "@better-auth/passkey/client";
|
|
|
7
7
|
import { emailOTPClient, magicLinkClient } from "better-auth/client/plugins";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
//#region src/auth/client.ts
|
|
10
|
+
/**
|
|
11
|
+
* Recover the user carried by a slug-scoped JWT.
|
|
12
|
+
*
|
|
13
|
+
* Gated `*.omgs.app` deploys receive their identity through the edge's
|
|
14
|
+
* `omg_access` token exchange, not through an app-local Better Auth session.
|
|
15
|
+
* The exchanged JWT is signed and verified by the server before it can be
|
|
16
|
+
* used for data access; decoding it here only lets the React provider expose
|
|
17
|
+
* that already-authenticated identity to `useUser()` and auth guards.
|
|
18
|
+
*/
|
|
19
|
+
function userFromToken(token) {
|
|
20
|
+
if (!token) return null;
|
|
21
|
+
try {
|
|
22
|
+
const payload = token.split(".")[1];
|
|
23
|
+
if (!payload) return null;
|
|
24
|
+
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/");
|
|
25
|
+
const padded = base64 + "=".repeat((4 - base64.length % 4) % 4);
|
|
26
|
+
const claims = JSON.parse(atob(padded));
|
|
27
|
+
if (typeof claims.sub !== "string" || typeof claims.email !== "string") return null;
|
|
28
|
+
return {
|
|
29
|
+
id: claims.sub,
|
|
30
|
+
email: claims.email,
|
|
31
|
+
...typeof claims.name === "string" && claims.name ? { name: claims.name } : {}
|
|
32
|
+
};
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** Prefer an explicit app session, then fall back to the edge-owned identity. */
|
|
38
|
+
function resolveAuthUser(sessionUser, tokenUser) {
|
|
39
|
+
return sessionUser ?? tokenUser;
|
|
40
|
+
}
|
|
10
41
|
const DEFAULT_AUTH_URL = "https://auth.omg.dev";
|
|
11
42
|
const AUTH_BRIDGE_PREFIX = "/__vibes/auth";
|
|
12
43
|
/**
|
|
@@ -454,19 +485,21 @@ function VibesAuthProvider({ client: providedClient, appId, authUrl, autoPrompt
|
|
|
454
485
|
]);
|
|
455
486
|
const { data: session, isPending } = client.authClient.useSession();
|
|
456
487
|
const [token, setToken] = useState(null);
|
|
488
|
+
const [tokenUser, setTokenUser] = useState(null);
|
|
457
489
|
const [authReady, setAuthReady] = useState(false);
|
|
490
|
+
const user = resolveAuthUser(session?.user ? {
|
|
491
|
+
id: session.user.id,
|
|
492
|
+
email: session.user.email,
|
|
493
|
+
name: session.user.name ?? void 0
|
|
494
|
+
} : null, tokenUser);
|
|
458
495
|
useEffect(() => {
|
|
459
496
|
if (isPending) return;
|
|
460
|
-
|
|
461
|
-
setToken(null);
|
|
462
|
-
client.clearToken();
|
|
463
|
-
setAuthReady(true);
|
|
464
|
-
return;
|
|
465
|
-
}
|
|
497
|
+
setAuthReady(false);
|
|
466
498
|
let cancelled = false;
|
|
467
499
|
client.getToken().then((t) => {
|
|
468
500
|
if (cancelled) return;
|
|
469
501
|
setToken(t);
|
|
502
|
+
setTokenUser(userFromToken(t));
|
|
470
503
|
setAuthReady(true);
|
|
471
504
|
});
|
|
472
505
|
return () => {
|
|
@@ -479,18 +512,14 @@ function VibesAuthProvider({ client: providedClient, appId, authUrl, autoPrompt
|
|
|
479
512
|
]);
|
|
480
513
|
useEffect(() => {
|
|
481
514
|
setAuthContext({
|
|
482
|
-
user
|
|
483
|
-
id: session.user.id,
|
|
484
|
-
email: session.user.email,
|
|
485
|
-
name: session.user.name ?? void 0
|
|
486
|
-
} : null,
|
|
515
|
+
user,
|
|
487
516
|
token,
|
|
488
517
|
authReady
|
|
489
518
|
});
|
|
490
519
|
}, [
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
520
|
+
user?.id,
|
|
521
|
+
user?.email,
|
|
522
|
+
user?.name,
|
|
494
523
|
token,
|
|
495
524
|
authReady
|
|
496
525
|
]);
|
|
@@ -498,27 +527,27 @@ function VibesAuthProvider({ client: providedClient, appId, authUrl, autoPrompt
|
|
|
498
527
|
await client.authClient.signOut();
|
|
499
528
|
client.clearToken();
|
|
500
529
|
setToken(null);
|
|
530
|
+
setTokenUser(null);
|
|
501
531
|
}, [client]);
|
|
502
532
|
const refreshToken = useCallback(async () => {
|
|
503
533
|
const t = await client.getToken();
|
|
504
534
|
setToken(t);
|
|
535
|
+
setTokenUser(userFromToken(t));
|
|
505
536
|
return t;
|
|
506
537
|
}, [client]);
|
|
507
|
-
const
|
|
508
|
-
id: session.user.id,
|
|
509
|
-
email: session.user.email,
|
|
510
|
-
name: session.user.name ?? void 0
|
|
511
|
-
} : null;
|
|
538
|
+
const loading = isPending || !authReady;
|
|
512
539
|
const value = useMemo(() => ({
|
|
513
540
|
user,
|
|
514
|
-
loading
|
|
541
|
+
loading,
|
|
515
542
|
token,
|
|
516
543
|
signOut,
|
|
517
544
|
refreshToken,
|
|
518
545
|
client
|
|
519
546
|
}), [
|
|
520
547
|
user?.id,
|
|
521
|
-
|
|
548
|
+
user?.email,
|
|
549
|
+
user?.name,
|
|
550
|
+
loading,
|
|
522
551
|
token,
|
|
523
552
|
signOut,
|
|
524
553
|
refreshToken,
|
package/package.json
CHANGED
package/src/auth/client.ts
CHANGED
|
@@ -10,6 +10,48 @@ export interface VibesUser {
|
|
|
10
10
|
name?: string
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Recover the user carried by a slug-scoped JWT.
|
|
15
|
+
*
|
|
16
|
+
* Gated `*.omgs.app` deploys receive their identity through the edge's
|
|
17
|
+
* `omg_access` token exchange, not through an app-local Better Auth session.
|
|
18
|
+
* The exchanged JWT is signed and verified by the server before it can be
|
|
19
|
+
* used for data access; decoding it here only lets the React provider expose
|
|
20
|
+
* that already-authenticated identity to `useUser()` and auth guards.
|
|
21
|
+
*/
|
|
22
|
+
export function userFromToken(token: string | null): VibesUser | null {
|
|
23
|
+
if (!token) return null
|
|
24
|
+
try {
|
|
25
|
+
const payload = token.split(".")[1]
|
|
26
|
+
if (!payload) return null
|
|
27
|
+
const base64 = payload.replace(/-/g, "+").replace(/_/g, "/")
|
|
28
|
+
const padded = base64 + "=".repeat((4 - (base64.length % 4)) % 4)
|
|
29
|
+
const claims = JSON.parse(atob(padded)) as {
|
|
30
|
+
sub?: unknown
|
|
31
|
+
email?: unknown
|
|
32
|
+
name?: unknown
|
|
33
|
+
}
|
|
34
|
+
if (typeof claims.sub !== "string" || typeof claims.email !== "string") {
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
id: claims.sub,
|
|
39
|
+
email: claims.email,
|
|
40
|
+
...(typeof claims.name === "string" && claims.name ? { name: claims.name } : {}),
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
return null
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Prefer an explicit app session, then fall back to the edge-owned identity. */
|
|
48
|
+
export function resolveAuthUser(
|
|
49
|
+
sessionUser: VibesUser | null,
|
|
50
|
+
tokenUser: VibesUser | null,
|
|
51
|
+
): VibesUser | null {
|
|
52
|
+
return sessionUser ?? tokenUser
|
|
53
|
+
}
|
|
54
|
+
|
|
13
55
|
export interface VibesAuthConfig {
|
|
14
56
|
/** App ID registered with the auth service */
|
|
15
57
|
appId: string
|
package/src/auth/react.tsx
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { createContext, useContext, useEffect, useState, useCallback, useMemo, type ReactNode } from "react"
|
|
4
4
|
import type { VibesAuthClient, VibesUser } from "./client"
|
|
5
|
-
import { createVibesAuth, deriveAppId } from "./client"
|
|
5
|
+
import { createVibesAuth, deriveAppId, resolveAuthUser, userFromToken } from "./client"
|
|
6
6
|
import { setAuthContext } from "./bridge"
|
|
7
7
|
import { VibesAuthAutoPrompt } from "./auto-prompt"
|
|
8
8
|
|
|
@@ -53,27 +53,34 @@ export function VibesAuthProvider({
|
|
|
53
53
|
|
|
54
54
|
const { data: session, isPending } = client.authClient.useSession()
|
|
55
55
|
const [token, setToken] = useState<string | null>(null)
|
|
56
|
+
const [tokenUser, setTokenUser] = useState<VibesUser | null>(null)
|
|
56
57
|
// True once the session check + first getToken() attempt have settled. Data
|
|
57
58
|
// hooks read this (via the bridge) to tell a transient startup
|
|
58
59
|
// `auth_required` apart from a genuinely signed-out user.
|
|
59
60
|
const [authReady, setAuthReady] = useState(false)
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
61
|
+
const sessionUser: VibesUser | null = session?.user
|
|
62
|
+
? {
|
|
63
|
+
id: session.user.id,
|
|
64
|
+
email: session.user.email,
|
|
65
|
+
name: session.user.name ?? undefined,
|
|
66
|
+
}
|
|
67
|
+
: null
|
|
68
|
+
// One identity owner: a local Better Auth session when the app explicitly
|
|
69
|
+
// signed the visitor in, otherwise the edge-exchanged OMG identity.
|
|
70
|
+
const user = resolveAuthUser(sessionUser, tokenUser)
|
|
71
|
+
|
|
72
|
+
// Resolve the slug JWT after the local session check settles. This request
|
|
73
|
+
// is intentionally unconditional: gated omgs.app deploys authenticate at
|
|
74
|
+
// the edge and exchange their first-party `omg_access` cookie for this JWT,
|
|
75
|
+
// so they do not (and should not) need a second app-local auth session.
|
|
65
76
|
useEffect(() => {
|
|
66
77
|
if (isPending) return
|
|
67
|
-
|
|
68
|
-
setToken(null)
|
|
69
|
-
client.clearToken()
|
|
70
|
-
setAuthReady(true)
|
|
71
|
-
return
|
|
72
|
-
}
|
|
78
|
+
setAuthReady(false)
|
|
73
79
|
let cancelled = false
|
|
74
80
|
client.getToken().then((t) => {
|
|
75
81
|
if (cancelled) return
|
|
76
82
|
setToken(t)
|
|
83
|
+
setTokenUser(userFromToken(t))
|
|
77
84
|
setAuthReady(true)
|
|
78
85
|
})
|
|
79
86
|
return () => {
|
|
@@ -85,41 +92,31 @@ export function VibesAuthProvider({
|
|
|
85
92
|
// doesn't take a client prop) can attach Authorization on every fetch.
|
|
86
93
|
useEffect(() => {
|
|
87
94
|
setAuthContext({
|
|
88
|
-
user
|
|
89
|
-
? {
|
|
90
|
-
id: session.user.id,
|
|
91
|
-
email: session.user.email,
|
|
92
|
-
name: session.user.name ?? undefined,
|
|
93
|
-
}
|
|
94
|
-
: null,
|
|
95
|
+
user,
|
|
95
96
|
token,
|
|
96
97
|
authReady,
|
|
97
98
|
})
|
|
98
|
-
}, [
|
|
99
|
+
}, [user?.id, user?.email, user?.name, token, authReady])
|
|
99
100
|
|
|
100
101
|
const signOut = useCallback(async () => {
|
|
101
102
|
await client.authClient.signOut()
|
|
102
103
|
client.clearToken()
|
|
103
104
|
setToken(null)
|
|
105
|
+
setTokenUser(null)
|
|
104
106
|
}, [client])
|
|
105
107
|
|
|
106
108
|
const refreshToken = useCallback(async () => {
|
|
107
109
|
const t = await client.getToken()
|
|
108
110
|
setToken(t)
|
|
111
|
+
setTokenUser(userFromToken(t))
|
|
109
112
|
return t
|
|
110
113
|
}, [client])
|
|
111
114
|
|
|
112
|
-
const
|
|
113
|
-
? {
|
|
114
|
-
id: session.user.id,
|
|
115
|
-
email: session.user.email,
|
|
116
|
-
name: session.user.name ?? undefined,
|
|
117
|
-
}
|
|
118
|
-
: null
|
|
115
|
+
const loading = isPending || !authReady
|
|
119
116
|
|
|
120
117
|
const value = useMemo<VibesAuthContextValue>(
|
|
121
|
-
() => ({ user, loading
|
|
122
|
-
[user?.id,
|
|
118
|
+
() => ({ user, loading, token, signOut, refreshToken, client }),
|
|
119
|
+
[user?.id, user?.email, user?.name, loading, token, signOut, refreshToken, client]
|
|
123
120
|
)
|
|
124
121
|
|
|
125
122
|
return (
|
package/src/brand/OmgBadge.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useId, useState, type CSSProperties } from "react"
|
|
1
|
+
import { useEffect, useId, useState, type CSSProperties } from "react"
|
|
2
2
|
import { VibesFeedback } from "../feedback/VibesFeedback"
|
|
3
3
|
|
|
4
4
|
export interface OmgBadgeProps {
|
|
@@ -32,15 +32,23 @@ function feedbackBundled(): boolean {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
// ── Remix ("Make it mine") gating ────────────────────────────────────────────
|
|
35
|
-
// The badge's first job on a
|
|
36
|
-
// visitor who only has the shared link can fork the app back on
|
|
37
|
-
//
|
|
38
|
-
//
|
|
35
|
+
// The badge's first job on a PUBLIC Explore-listed app is the recipient→creator
|
|
36
|
+
// hinge: a visitor who only has the shared link can fork the app back on
|
|
37
|
+
// omg.dev. Private (not listed) apps keep the plain branding badge only —
|
|
38
|
+
// remixBySlug rejects them, so the CTA must never appear. Once dismissed, the
|
|
39
|
+
// SAME badge collapses to branding — the omg mark never leaves, only the CTA.
|
|
39
40
|
const DISMISS_KEY = "vibes-remix:dismissed"
|
|
40
41
|
const REMIX_ORIGIN =
|
|
41
42
|
(typeof import.meta !== "undefined" &&
|
|
42
43
|
(import.meta as { env?: Record<string, string | undefined> }).env?.VITE_REMIX_ORIGIN) ||
|
|
43
44
|
"https://omg.dev"
|
|
45
|
+
// Control-plane host for the unauthenticated isRemixable probe. Overridable for
|
|
46
|
+
// self-host; defaults to production. CORS is open for this path only (Caddy).
|
|
47
|
+
const CONTROLPLANE_URL = (
|
|
48
|
+
(typeof import.meta !== "undefined" &&
|
|
49
|
+
(import.meta as { env?: Record<string, string | undefined> }).env?.VITE_CONTROLPLANE_URL) ||
|
|
50
|
+
"https://backend.omg.dev"
|
|
51
|
+
).replace(/\/$/, "")
|
|
44
52
|
|
|
45
53
|
function appSlug(): string | null {
|
|
46
54
|
if (typeof import.meta === "undefined") return null
|
|
@@ -66,20 +74,49 @@ function setDismissedFlag() {
|
|
|
66
74
|
}
|
|
67
75
|
}
|
|
68
76
|
|
|
77
|
+
/** Live check: only Explore-listed public apps may show "Make it mine". Fail closed. */
|
|
78
|
+
async function fetchRemixable(slug: string): Promise<boolean> {
|
|
79
|
+
try {
|
|
80
|
+
const res = await fetch(`${CONTROLPLANE_URL}/api/projects/isRemixable`, {
|
|
81
|
+
method: "POST",
|
|
82
|
+
headers: { "content-type": "application/json" },
|
|
83
|
+
body: JSON.stringify({ slug }),
|
|
84
|
+
// Public boolean; no credentials. Avoid caching a stale private→public flip.
|
|
85
|
+
cache: "no-store",
|
|
86
|
+
})
|
|
87
|
+
if (!res.ok) return false
|
|
88
|
+
const data = (await res.json()) as { remixable?: unknown }
|
|
89
|
+
return data?.remixable === true
|
|
90
|
+
} catch {
|
|
91
|
+
return false
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
69
95
|
export function OmgBadge({
|
|
70
96
|
href = "https://omg.dev",
|
|
71
97
|
label = "What is omg?",
|
|
72
98
|
}: OmgBadgeProps) {
|
|
73
99
|
const [open, setOpen] = useState(false)
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
100
|
+
// Fail closed: start as branding. Promote to remix only after the public
|
|
101
|
+
// catalog confirms this slug is listed — never flash "Make it mine" on a
|
|
102
|
+
// private published app while the probe is in flight.
|
|
77
103
|
const slug = appSlug()
|
|
78
|
-
const [remix, setRemix] = useState(
|
|
104
|
+
const [remix, setRemix] = useState(false)
|
|
79
105
|
const titleId = useId()
|
|
80
106
|
const descriptionId = useId()
|
|
81
107
|
const markId = useId().replace(/[^a-zA-Z0-9_-]/g, "")
|
|
82
108
|
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
if (!slug || isDismissed()) return
|
|
111
|
+
let cancelled = false
|
|
112
|
+
void fetchRemixable(slug).then((ok) => {
|
|
113
|
+
if (!cancelled && ok && !isDismissed()) setRemix(true)
|
|
114
|
+
})
|
|
115
|
+
return () => {
|
|
116
|
+
cancelled = true
|
|
117
|
+
}
|
|
118
|
+
}, [slug])
|
|
119
|
+
|
|
83
120
|
function dismissRemix() {
|
|
84
121
|
setDismissedFlag()
|
|
85
122
|
setRemix(false)
|
package/src/brand/auto.tsx
CHANGED
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
// generated app to carry attribution code in its source.
|
|
6
6
|
//
|
|
7
7
|
// One control, not three: this badge is the "Make it mine" remix CTA on a fresh
|
|
8
|
-
// visit (same omg mark), collapses to the plain
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
8
|
+
// visit to a PUBLIC (Explore-listed) app (same omg mark), collapses to the plain
|
|
9
|
+
// branding badge on dismiss or when the app is private, and bundles the
|
|
10
|
+
// (button-less) feedback sheet, summoned from its dialog. The vite-plugin
|
|
11
|
+
// therefore skips the standalone feedback/auto + remix-cta imports whenever
|
|
12
|
+
// this badge is enabled (the default).
|
|
12
13
|
|
|
13
14
|
import { createRoot } from "react-dom/client"
|
|
14
15
|
import { OmgBadge } from "./OmgBadge"
|