@omg-dev/sdk 0.4.29 → 0.4.31
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-C559Dp1f.mjs +222 -0
- package/dist/brand/auto.mjs +1 -2
- package/dist/index.mjs +203 -23
- package/package.json +2 -7
- package/src/brand/OmgBadge.tsx +10 -194
- package/src/brand/auto.tsx +0 -9
- package/src/index.ts +0 -3
- package/src/sandbox.test.ts +4 -0
- package/src/sandbox.ts +1 -0
- package/dist/OmgBadge-CDnTIlnn.mjs +0 -371
- package/dist/VibesFeedback-BF2Vf6FK.mjs +0 -808
- package/dist/feedback/auto.mjs +0 -26
- package/src/feedback/VibesFeedback.tsx +0 -360
- package/src/feedback/auto.tsx +0 -47
- package/src/feedback/gestures.ts +0 -296
- package/src/feedback/index.ts +0 -18
- package/src/feedback/screenshot.ts +0 -37
- package/src/feedback/trace.ts +0 -166
package/src/brand/OmgBadge.tsx
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { VibesFeedback } from "../feedback/VibesFeedback"
|
|
1
|
+
import { useId, useState, type CSSProperties } from "react"
|
|
3
2
|
|
|
4
3
|
export interface OmgBadgeProps {
|
|
5
4
|
/** Link used by the dialog CTA. Default: https://omg.dev */
|
|
@@ -13,169 +12,29 @@ const BRAND = "#FF5530"
|
|
|
13
12
|
const Z_BADGE = 2147482000
|
|
14
13
|
const Z_DIALOG = 2147483100
|
|
15
14
|
|
|
16
|
-
// Cross-process signal the brand badge dispatches to summon the (button-less)
|
|
17
|
-
// feedback sheet it mounts. Bundling feedback INTO this badge is deliberate:
|
|
18
|
-
// one floating control in the corner, not three.
|
|
19
|
-
export const FEEDBACK_OPEN_EVENT = "vibes:feedback:open"
|
|
20
|
-
|
|
21
|
-
// Build-time gate (set by @omg-dev/vite-plugin via `define`). True when the app
|
|
22
|
-
// has feedback enabled AND the brand badge owns it. Falls back to true when the
|
|
23
|
-
// badge is used outside the plugin (it's only auto-injected by the plugin, so
|
|
24
|
-
// in practice the define is always present in published bundles).
|
|
25
|
-
declare const __VIBES_BRAND_FEEDBACK__: boolean | undefined
|
|
26
|
-
function feedbackBundled(): boolean {
|
|
27
|
-
try {
|
|
28
|
-
return typeof __VIBES_BRAND_FEEDBACK__ === "undefined" ? true : __VIBES_BRAND_FEEDBACK__
|
|
29
|
-
} catch {
|
|
30
|
-
return true
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// ── Remix ("Make it mine") gating ────────────────────────────────────────────
|
|
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.
|
|
40
|
-
const DISMISS_KEY = "vibes-remix:dismissed"
|
|
41
|
-
const REMIX_ORIGIN =
|
|
42
|
-
(typeof import.meta !== "undefined" &&
|
|
43
|
-
(import.meta as { env?: Record<string, string | undefined> }).env?.VITE_REMIX_ORIGIN) ||
|
|
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(/\/$/, "")
|
|
52
|
-
|
|
53
|
-
function appSlug(): string | null {
|
|
54
|
-
if (typeof import.meta === "undefined") return null
|
|
55
|
-
const env = (import.meta as { env?: Record<string, string | undefined> }).env
|
|
56
|
-
const slug = env?.VITE_APP_SLUG?.trim()
|
|
57
|
-
if (!slug || !/^[a-z0-9-]+$/.test(slug)) return null
|
|
58
|
-
return slug
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function isDismissed(): boolean {
|
|
62
|
-
try {
|
|
63
|
-
return localStorage.getItem(DISMISS_KEY) === "1"
|
|
64
|
-
} catch {
|
|
65
|
-
return false
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function setDismissedFlag() {
|
|
70
|
-
try {
|
|
71
|
-
localStorage.setItem(DISMISS_KEY, "1")
|
|
72
|
-
} catch {
|
|
73
|
-
/* private mode */
|
|
74
|
-
}
|
|
75
|
-
}
|
|
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
|
-
|
|
95
15
|
export function OmgBadge({
|
|
96
16
|
href = "https://omg.dev",
|
|
97
17
|
label = "What is omg?",
|
|
98
18
|
}: OmgBadgeProps) {
|
|
99
19
|
const [open, setOpen] = useState(false)
|
|
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.
|
|
103
|
-
const slug = appSlug()
|
|
104
|
-
const [remix, setRemix] = useState(false)
|
|
105
20
|
const titleId = useId()
|
|
106
21
|
const descriptionId = useId()
|
|
107
22
|
const markId = useId().replace(/[^a-zA-Z0-9_-]/g, "")
|
|
108
23
|
|
|
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
|
-
|
|
120
|
-
function dismissRemix() {
|
|
121
|
-
setDismissedFlag()
|
|
122
|
-
setRemix(false)
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function summonFeedback() {
|
|
126
|
-
setOpen(false)
|
|
127
|
-
if (typeof window !== "undefined") {
|
|
128
|
-
window.dispatchEvent(new CustomEvent(FEEDBACK_OPEN_EVENT))
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
24
|
return (
|
|
133
25
|
<>
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
>
|
|
144
|
-
<OmgMark size={18} id={`remix-${markId}`} />
|
|
145
|
-
<span style={badgeTextStyle}>Make it mine</span>
|
|
146
|
-
<button
|
|
147
|
-
type="button"
|
|
148
|
-
aria-label="Dismiss"
|
|
149
|
-
onClick={(event) => {
|
|
150
|
-
event.preventDefault()
|
|
151
|
-
event.stopPropagation()
|
|
152
|
-
dismissRemix()
|
|
153
|
-
}}
|
|
154
|
-
style={dismissStyle}
|
|
155
|
-
>
|
|
156
|
-
<svg aria-hidden="true" width="13" height="13" viewBox="0 0 24 24" fill="none">
|
|
157
|
-
<path d="M6 6l12 12M18 6 6 18" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" />
|
|
158
|
-
</svg>
|
|
159
|
-
</button>
|
|
160
|
-
</a>
|
|
161
|
-
) : (
|
|
162
|
-
// Branding state: the omg mark + "omg". Opens the "Built with omg"
|
|
163
|
-
// dialog, which also hosts the feedback entry.
|
|
164
|
-
<button
|
|
165
|
-
type="button"
|
|
166
|
-
aria-label={label}
|
|
167
|
-
data-vibes-feedback=""
|
|
168
|
-
onClick={() => setOpen(true)}
|
|
169
|
-
style={badgeStyle}
|
|
170
|
-
>
|
|
171
|
-
<OmgMark size={18} id={`badge-${markId}`} />
|
|
172
|
-
<span style={badgeTextStyle}>omg</span>
|
|
173
|
-
</button>
|
|
174
|
-
)}
|
|
26
|
+
<button
|
|
27
|
+
type="button"
|
|
28
|
+
aria-label={label}
|
|
29
|
+
onClick={() => setOpen(true)}
|
|
30
|
+
style={badgeStyle}
|
|
31
|
+
>
|
|
32
|
+
<OmgMark size={18} id={`badge-${markId}`} />
|
|
33
|
+
<span style={badgeTextStyle}>omg</span>
|
|
34
|
+
</button>
|
|
175
35
|
|
|
176
36
|
{open && (
|
|
177
37
|
<div
|
|
178
|
-
data-vibes-feedback=""
|
|
179
38
|
role="presentation"
|
|
180
39
|
style={backdropStyle}
|
|
181
40
|
onMouseDown={(event) => {
|
|
@@ -213,20 +72,9 @@ export function OmgBadge({
|
|
|
213
72
|
<a href={href} target="_blank" rel="noopener noreferrer" style={ctaStyle}>
|
|
214
73
|
Learn about omg
|
|
215
74
|
</a>
|
|
216
|
-
{feedbackBundled() && (
|
|
217
|
-
<button type="button" onClick={summonFeedback} style={secondaryStyle}>
|
|
218
|
-
Send feedback
|
|
219
|
-
</button>
|
|
220
|
-
)}
|
|
221
75
|
</div>
|
|
222
76
|
</div>
|
|
223
77
|
)}
|
|
224
|
-
|
|
225
|
-
{/* Feedback is bundled into the badge — a button-less sheet summoned from
|
|
226
|
-
the dialog (or by gesture), so there's no second floating control. */}
|
|
227
|
-
{feedbackBundled() && (
|
|
228
|
-
<VibesFeedback showButton={false} accent={BRAND} title="Send feedback" />
|
|
229
|
-
)}
|
|
230
78
|
</>
|
|
231
79
|
)
|
|
232
80
|
}
|
|
@@ -288,20 +136,6 @@ const badgeTextStyle: CSSProperties = {
|
|
|
288
136
|
padding: "0 3px",
|
|
289
137
|
}
|
|
290
138
|
|
|
291
|
-
const dismissStyle: CSSProperties = {
|
|
292
|
-
width: 22,
|
|
293
|
-
height: 22,
|
|
294
|
-
flex: "0 0 auto",
|
|
295
|
-
borderRadius: 999,
|
|
296
|
-
border: "none",
|
|
297
|
-
background: "rgba(20, 16, 12, 0.06)",
|
|
298
|
-
color: "#6b6459",
|
|
299
|
-
display: "grid",
|
|
300
|
-
placeItems: "center",
|
|
301
|
-
cursor: "pointer",
|
|
302
|
-
padding: 0,
|
|
303
|
-
}
|
|
304
|
-
|
|
305
139
|
const backdropStyle: CSSProperties = {
|
|
306
140
|
position: "fixed",
|
|
307
141
|
inset: 0,
|
|
@@ -383,21 +217,3 @@ const ctaStyle: CSSProperties = {
|
|
|
383
217
|
textDecoration: "none",
|
|
384
218
|
letterSpacing: 0,
|
|
385
219
|
}
|
|
386
|
-
|
|
387
|
-
const secondaryStyle: CSSProperties = {
|
|
388
|
-
display: "inline-flex",
|
|
389
|
-
alignItems: "center",
|
|
390
|
-
justifyContent: "center",
|
|
391
|
-
width: "100%",
|
|
392
|
-
height: 44,
|
|
393
|
-
marginTop: 8,
|
|
394
|
-
borderRadius: 12,
|
|
395
|
-
background: "transparent",
|
|
396
|
-
border: "1px solid rgba(21, 17, 13, 0.14)",
|
|
397
|
-
color: "#15110d",
|
|
398
|
-
fontSize: 15,
|
|
399
|
-
fontWeight: 650,
|
|
400
|
-
cursor: "pointer",
|
|
401
|
-
fontFamily: font,
|
|
402
|
-
letterSpacing: 0,
|
|
403
|
-
}
|
package/src/brand/auto.tsx
CHANGED
|
@@ -4,13 +4,6 @@
|
|
|
4
4
|
// app's React tree so it survives user app rewrites and does not require each
|
|
5
5
|
// generated app to carry attribution code in its source.
|
|
6
6
|
//
|
|
7
|
-
// One control, not three: this badge is the "Make it mine" remix CTA on a fresh
|
|
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).
|
|
13
|
-
|
|
14
7
|
import { createRoot } from "react-dom/client"
|
|
15
8
|
import { OmgBadge } from "./OmgBadge"
|
|
16
9
|
|
|
@@ -21,8 +14,6 @@ function mount() {
|
|
|
21
14
|
if (document.getElementById(HOST_ID)) return
|
|
22
15
|
const host = document.createElement("div")
|
|
23
16
|
host.id = HOST_ID
|
|
24
|
-
// Feedback screenshots should capture the app, not platform chrome.
|
|
25
|
-
host.setAttribute("data-vibes-feedback", "")
|
|
26
17
|
document.body.appendChild(host)
|
|
27
18
|
createRoot(host).render(<OmgBadge />)
|
|
28
19
|
}
|
package/src/index.ts
CHANGED
|
@@ -14,9 +14,6 @@ export * from "./auth/index"
|
|
|
14
14
|
// Storage surface — useUpload() + <VibesUpload />. Re-exported here for
|
|
15
15
|
// the same publish-workflow reason as auth (only "." subpath survives).
|
|
16
16
|
export * from "./storage/index"
|
|
17
|
-
// Feedback surface — <VibesFeedback /> gesture widget + trace capture.
|
|
18
|
-
// Re-exported here for the same publish-workflow reason (only "." survives).
|
|
19
|
-
export * from "./feedback/index"
|
|
20
17
|
// Brand surface — <OmgBadge /> platform attribution badge.
|
|
21
18
|
export * from "./brand/index"
|
|
22
19
|
// Notifications surface — durable in-app inbox + Web Push registration.
|
package/src/sandbox.test.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from "vitest";
|
|
2
2
|
import { createSandbox, forkSandbox, runtimeClaims } from "./sandbox";
|
|
3
3
|
|
|
4
|
+
it("exports the funded transcription capability", () => {
|
|
5
|
+
expect(runtimeClaims.speechTranscribe).toBe("speech.transcribe");
|
|
6
|
+
});
|
|
7
|
+
|
|
4
8
|
describe("sandbox SDK", () => {
|
|
5
9
|
it("posts create requests to the sandbox router", async () => {
|
|
6
10
|
const calls: Array<{ url: string; body: unknown }> = [];
|
package/src/sandbox.ts
CHANGED
|
@@ -1,371 +0,0 @@
|
|
|
1
|
-
import { t as VibesFeedback } from "./VibesFeedback-BF2Vf6FK.mjs";
|
|
2
|
-
import { useEffect, useId, useState } from "react";
|
|
3
|
-
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
-
//#region src/brand/OmgBadge.tsx
|
|
5
|
-
const BRAND = "#FF5530";
|
|
6
|
-
const Z_BADGE = 2147482e3;
|
|
7
|
-
const Z_DIALOG = 2147483100;
|
|
8
|
-
const FEEDBACK_OPEN_EVENT = "vibes:feedback:open";
|
|
9
|
-
function feedbackBundled() {
|
|
10
|
-
try {
|
|
11
|
-
return typeof __VIBES_BRAND_FEEDBACK__ === "undefined" ? true : __VIBES_BRAND_FEEDBACK__;
|
|
12
|
-
} catch {
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
const DISMISS_KEY = "vibes-remix:dismissed";
|
|
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(/\/$/, "");
|
|
19
|
-
function appSlug() {
|
|
20
|
-
if (typeof import.meta === "undefined") return null;
|
|
21
|
-
const slug = import.meta.env?.VITE_APP_SLUG?.trim();
|
|
22
|
-
if (!slug || !/^[a-z0-9-]+$/.test(slug)) return null;
|
|
23
|
-
return slug;
|
|
24
|
-
}
|
|
25
|
-
function isDismissed() {
|
|
26
|
-
try {
|
|
27
|
-
return localStorage.getItem(DISMISS_KEY) === "1";
|
|
28
|
-
} catch {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function setDismissedFlag() {
|
|
33
|
-
try {
|
|
34
|
-
localStorage.setItem(DISMISS_KEY, "1");
|
|
35
|
-
} catch {}
|
|
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
|
-
}
|
|
52
|
-
function OmgBadge({ href = "https://omg.dev", label = "What is omg?" }) {
|
|
53
|
-
const [open, setOpen] = useState(false);
|
|
54
|
-
const slug = appSlug();
|
|
55
|
-
const [remix, setRemix] = useState(false);
|
|
56
|
-
const titleId = useId();
|
|
57
|
-
const descriptionId = useId();
|
|
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]);
|
|
69
|
-
function dismissRemix() {
|
|
70
|
-
setDismissedFlag();
|
|
71
|
-
setRemix(false);
|
|
72
|
-
}
|
|
73
|
-
function summonFeedback() {
|
|
74
|
-
setOpen(false);
|
|
75
|
-
if (typeof window !== "undefined") window.dispatchEvent(new CustomEvent(FEEDBACK_OPEN_EVENT));
|
|
76
|
-
}
|
|
77
|
-
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
78
|
-
remix && slug ? /* @__PURE__ */ jsxs("a", {
|
|
79
|
-
href: `${REMIX_ORIGIN}/remix/${slug}?ref=pwa`,
|
|
80
|
-
"aria-label": "Make this app mine",
|
|
81
|
-
"data-vibes-feedback": "",
|
|
82
|
-
style: badgeStyle,
|
|
83
|
-
children: [
|
|
84
|
-
/* @__PURE__ */ jsx(OmgMark, {
|
|
85
|
-
size: 18,
|
|
86
|
-
id: `remix-${markId}`
|
|
87
|
-
}),
|
|
88
|
-
/* @__PURE__ */ jsx("span", {
|
|
89
|
-
style: badgeTextStyle,
|
|
90
|
-
children: "Make it mine"
|
|
91
|
-
}),
|
|
92
|
-
/* @__PURE__ */ jsx("button", {
|
|
93
|
-
type: "button",
|
|
94
|
-
"aria-label": "Dismiss",
|
|
95
|
-
onClick: (event) => {
|
|
96
|
-
event.preventDefault();
|
|
97
|
-
event.stopPropagation();
|
|
98
|
-
dismissRemix();
|
|
99
|
-
},
|
|
100
|
-
style: dismissStyle,
|
|
101
|
-
children: /* @__PURE__ */ jsx("svg", {
|
|
102
|
-
"aria-hidden": "true",
|
|
103
|
-
width: "13",
|
|
104
|
-
height: "13",
|
|
105
|
-
viewBox: "0 0 24 24",
|
|
106
|
-
fill: "none",
|
|
107
|
-
children: /* @__PURE__ */ jsx("path", {
|
|
108
|
-
d: "M6 6l12 12M18 6 6 18",
|
|
109
|
-
stroke: "currentColor",
|
|
110
|
-
strokeWidth: "2.4",
|
|
111
|
-
strokeLinecap: "round"
|
|
112
|
-
})
|
|
113
|
-
})
|
|
114
|
-
})
|
|
115
|
-
]
|
|
116
|
-
}) : /* @__PURE__ */ jsxs("button", {
|
|
117
|
-
type: "button",
|
|
118
|
-
"aria-label": label,
|
|
119
|
-
"data-vibes-feedback": "",
|
|
120
|
-
onClick: () => setOpen(true),
|
|
121
|
-
style: badgeStyle,
|
|
122
|
-
children: [/* @__PURE__ */ jsx(OmgMark, {
|
|
123
|
-
size: 18,
|
|
124
|
-
id: `badge-${markId}`
|
|
125
|
-
}), /* @__PURE__ */ jsx("span", {
|
|
126
|
-
style: badgeTextStyle,
|
|
127
|
-
children: "omg"
|
|
128
|
-
})]
|
|
129
|
-
}),
|
|
130
|
-
open && /* @__PURE__ */ jsx("div", {
|
|
131
|
-
"data-vibes-feedback": "",
|
|
132
|
-
role: "presentation",
|
|
133
|
-
style: backdropStyle,
|
|
134
|
-
onMouseDown: (event) => {
|
|
135
|
-
if (event.target === event.currentTarget) setOpen(false);
|
|
136
|
-
},
|
|
137
|
-
children: /* @__PURE__ */ jsxs("div", {
|
|
138
|
-
role: "dialog",
|
|
139
|
-
"aria-modal": "true",
|
|
140
|
-
"aria-labelledby": titleId,
|
|
141
|
-
"aria-describedby": descriptionId,
|
|
142
|
-
style: dialogStyle,
|
|
143
|
-
children: [
|
|
144
|
-
/* @__PURE__ */ jsx("button", {
|
|
145
|
-
type: "button",
|
|
146
|
-
"aria-label": "Close",
|
|
147
|
-
onClick: () => setOpen(false),
|
|
148
|
-
style: closeButtonStyle,
|
|
149
|
-
children: /* @__PURE__ */ jsx("svg", {
|
|
150
|
-
"aria-hidden": "true",
|
|
151
|
-
width: "18",
|
|
152
|
-
height: "18",
|
|
153
|
-
viewBox: "0 0 24 24",
|
|
154
|
-
fill: "none",
|
|
155
|
-
children: /* @__PURE__ */ jsx("path", {
|
|
156
|
-
d: "M6 6l12 12M18 6 6 18",
|
|
157
|
-
stroke: "currentColor",
|
|
158
|
-
strokeWidth: "2",
|
|
159
|
-
strokeLinecap: "round"
|
|
160
|
-
})
|
|
161
|
-
})
|
|
162
|
-
}),
|
|
163
|
-
/* @__PURE__ */ jsx("div", {
|
|
164
|
-
style: markWrapStyle,
|
|
165
|
-
children: /* @__PURE__ */ jsx(OmgMark, {
|
|
166
|
-
size: 34,
|
|
167
|
-
id: `dialog-${markId}`
|
|
168
|
-
})
|
|
169
|
-
}),
|
|
170
|
-
/* @__PURE__ */ jsx("h2", {
|
|
171
|
-
id: titleId,
|
|
172
|
-
style: titleStyle,
|
|
173
|
-
children: "Built with omg"
|
|
174
|
-
}),
|
|
175
|
-
/* @__PURE__ */ jsx("p", {
|
|
176
|
-
id: descriptionId,
|
|
177
|
-
style: bodyStyle,
|
|
178
|
-
children: "omg turns a plain-language idea into a real app, then hosts it with sign-in, data storage, and publishing already handled."
|
|
179
|
-
}),
|
|
180
|
-
/* @__PURE__ */ jsx("a", {
|
|
181
|
-
href,
|
|
182
|
-
target: "_blank",
|
|
183
|
-
rel: "noopener noreferrer",
|
|
184
|
-
style: ctaStyle,
|
|
185
|
-
children: "Learn about omg"
|
|
186
|
-
}),
|
|
187
|
-
feedbackBundled() && /* @__PURE__ */ jsx("button", {
|
|
188
|
-
type: "button",
|
|
189
|
-
onClick: summonFeedback,
|
|
190
|
-
style: secondaryStyle,
|
|
191
|
-
children: "Send feedback"
|
|
192
|
-
})
|
|
193
|
-
]
|
|
194
|
-
})
|
|
195
|
-
}),
|
|
196
|
-
feedbackBundled() && /* @__PURE__ */ jsx(VibesFeedback, {
|
|
197
|
-
showButton: false,
|
|
198
|
-
accent: BRAND,
|
|
199
|
-
title: "Send feedback"
|
|
200
|
-
})
|
|
201
|
-
] });
|
|
202
|
-
}
|
|
203
|
-
function OmgMark({ size, id }) {
|
|
204
|
-
const maskId = `omg-mark-cut-${id}`;
|
|
205
|
-
return /* @__PURE__ */ jsxs("svg", {
|
|
206
|
-
"aria-hidden": "true",
|
|
207
|
-
width: size,
|
|
208
|
-
height: size,
|
|
209
|
-
viewBox: "0 0 100 100",
|
|
210
|
-
fill: "none",
|
|
211
|
-
style: {
|
|
212
|
-
display: "block",
|
|
213
|
-
flex: "0 0 auto"
|
|
214
|
-
},
|
|
215
|
-
children: [/* @__PURE__ */ jsxs("mask", {
|
|
216
|
-
id: maskId,
|
|
217
|
-
children: [/* @__PURE__ */ jsx("rect", {
|
|
218
|
-
width: "100",
|
|
219
|
-
height: "100",
|
|
220
|
-
fill: "white"
|
|
221
|
-
}), /* @__PURE__ */ jsx("circle", {
|
|
222
|
-
cx: "71",
|
|
223
|
-
cy: "29",
|
|
224
|
-
r: size <= 16 ? 16.5 : size <= 20 ? 15.2 : size <= 32 ? 14 : 13.2,
|
|
225
|
-
fill: "black"
|
|
226
|
-
})]
|
|
227
|
-
}), /* @__PURE__ */ jsx("circle", {
|
|
228
|
-
cx: "50",
|
|
229
|
-
cy: "50",
|
|
230
|
-
r: "44",
|
|
231
|
-
fill: BRAND,
|
|
232
|
-
mask: `url(#${maskId})`
|
|
233
|
-
})]
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
const font = "system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif";
|
|
237
|
-
const badgeStyle = {
|
|
238
|
-
position: "fixed",
|
|
239
|
-
right: "max(14px, env(safe-area-inset-right))",
|
|
240
|
-
bottom: "calc(16px + env(safe-area-inset-bottom))",
|
|
241
|
-
zIndex: Z_BADGE,
|
|
242
|
-
height: 38,
|
|
243
|
-
padding: "0 8px 0 10px",
|
|
244
|
-
border: "1px solid rgba(20, 16, 12, 0.12)",
|
|
245
|
-
borderRadius: 999,
|
|
246
|
-
background: "rgba(255, 255, 255, 0.94)",
|
|
247
|
-
color: "#15110d",
|
|
248
|
-
boxShadow: "0 8px 28px rgba(0, 0, 0, 0.16)",
|
|
249
|
-
display: "inline-flex",
|
|
250
|
-
alignItems: "center",
|
|
251
|
-
gap: 7,
|
|
252
|
-
fontFamily: font,
|
|
253
|
-
cursor: "pointer",
|
|
254
|
-
textDecoration: "none",
|
|
255
|
-
WebkitTapHighlightColor: "transparent",
|
|
256
|
-
backdropFilter: "blur(10px)"
|
|
257
|
-
};
|
|
258
|
-
const badgeTextStyle = {
|
|
259
|
-
fontSize: 13,
|
|
260
|
-
lineHeight: "18px",
|
|
261
|
-
fontWeight: 700,
|
|
262
|
-
letterSpacing: 0,
|
|
263
|
-
padding: "0 3px"
|
|
264
|
-
};
|
|
265
|
-
const dismissStyle = {
|
|
266
|
-
width: 22,
|
|
267
|
-
height: 22,
|
|
268
|
-
flex: "0 0 auto",
|
|
269
|
-
borderRadius: 999,
|
|
270
|
-
border: "none",
|
|
271
|
-
background: "rgba(20, 16, 12, 0.06)",
|
|
272
|
-
color: "#6b6459",
|
|
273
|
-
display: "grid",
|
|
274
|
-
placeItems: "center",
|
|
275
|
-
cursor: "pointer",
|
|
276
|
-
padding: 0
|
|
277
|
-
};
|
|
278
|
-
const backdropStyle = {
|
|
279
|
-
position: "fixed",
|
|
280
|
-
inset: 0,
|
|
281
|
-
zIndex: Z_DIALOG,
|
|
282
|
-
display: "grid",
|
|
283
|
-
placeItems: "center",
|
|
284
|
-
padding: "24px 16px calc(24px + env(safe-area-inset-bottom))",
|
|
285
|
-
background: "rgba(15, 13, 11, 0.34)",
|
|
286
|
-
boxSizing: "border-box"
|
|
287
|
-
};
|
|
288
|
-
const dialogStyle = {
|
|
289
|
-
position: "relative",
|
|
290
|
-
width: "min(100%, 360px)",
|
|
291
|
-
borderRadius: 18,
|
|
292
|
-
padding: "26px 22px 20px",
|
|
293
|
-
background: "#fffaf4",
|
|
294
|
-
color: "#15110d",
|
|
295
|
-
boxShadow: "0 24px 70px rgba(0, 0, 0, 0.28)",
|
|
296
|
-
border: "1px solid rgba(21, 17, 13, 0.08)",
|
|
297
|
-
fontFamily: font,
|
|
298
|
-
boxSizing: "border-box"
|
|
299
|
-
};
|
|
300
|
-
const closeButtonStyle = {
|
|
301
|
-
position: "absolute",
|
|
302
|
-
top: 10,
|
|
303
|
-
right: 10,
|
|
304
|
-
width: 34,
|
|
305
|
-
height: 34,
|
|
306
|
-
borderRadius: 999,
|
|
307
|
-
border: "none",
|
|
308
|
-
background: "transparent",
|
|
309
|
-
color: "#6b6459",
|
|
310
|
-
display: "grid",
|
|
311
|
-
placeItems: "center",
|
|
312
|
-
cursor: "pointer"
|
|
313
|
-
};
|
|
314
|
-
const markWrapStyle = {
|
|
315
|
-
width: 52,
|
|
316
|
-
height: 52,
|
|
317
|
-
borderRadius: 16,
|
|
318
|
-
display: "grid",
|
|
319
|
-
placeItems: "center",
|
|
320
|
-
background: "#fff",
|
|
321
|
-
boxShadow: "inset 0 0 0 1px rgba(21, 17, 13, 0.08)",
|
|
322
|
-
marginBottom: 16
|
|
323
|
-
};
|
|
324
|
-
const titleStyle = {
|
|
325
|
-
margin: 0,
|
|
326
|
-
color: "#15110d",
|
|
327
|
-
fontSize: 23,
|
|
328
|
-
lineHeight: "28px",
|
|
329
|
-
fontWeight: 750,
|
|
330
|
-
letterSpacing: 0
|
|
331
|
-
};
|
|
332
|
-
const bodyStyle = {
|
|
333
|
-
margin: "10px 0 18px",
|
|
334
|
-
color: "#5f574d",
|
|
335
|
-
fontSize: 15,
|
|
336
|
-
lineHeight: "22px",
|
|
337
|
-
letterSpacing: 0
|
|
338
|
-
};
|
|
339
|
-
const ctaStyle = {
|
|
340
|
-
display: "inline-flex",
|
|
341
|
-
alignItems: "center",
|
|
342
|
-
justifyContent: "center",
|
|
343
|
-
width: "100%",
|
|
344
|
-
height: 44,
|
|
345
|
-
borderRadius: 12,
|
|
346
|
-
background: "#15110d",
|
|
347
|
-
color: "#fffaf4",
|
|
348
|
-
fontSize: 15,
|
|
349
|
-
fontWeight: 700,
|
|
350
|
-
textDecoration: "none",
|
|
351
|
-
letterSpacing: 0
|
|
352
|
-
};
|
|
353
|
-
const secondaryStyle = {
|
|
354
|
-
display: "inline-flex",
|
|
355
|
-
alignItems: "center",
|
|
356
|
-
justifyContent: "center",
|
|
357
|
-
width: "100%",
|
|
358
|
-
height: 44,
|
|
359
|
-
marginTop: 8,
|
|
360
|
-
borderRadius: 12,
|
|
361
|
-
background: "transparent",
|
|
362
|
-
border: "1px solid rgba(21, 17, 13, 0.14)",
|
|
363
|
-
color: "#15110d",
|
|
364
|
-
fontSize: 15,
|
|
365
|
-
fontWeight: 650,
|
|
366
|
-
cursor: "pointer",
|
|
367
|
-
fontFamily: font,
|
|
368
|
-
letterSpacing: 0
|
|
369
|
-
};
|
|
370
|
-
//#endregion
|
|
371
|
-
export { OmgBadge as t };
|