@omg-dev/pwa 0.4.33 → 0.4.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/auto.mjs +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{react-BYbYeTts.mjs → react-BHavFwjG.mjs} +78 -2
- package/package.json +1 -1
- package/src/auto.tsx +2 -1
- package/src/core.ts +6 -0
- package/src/index.ts +1 -0
- package/src/react.tsx +42 -0
- package/src/styles.ts +42 -0
package/dist/auto.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as countVisit, c as isInIframe,
|
|
1
|
+
import { a as countVisit, c as isInIframe, f as setAutoShown, l as isIosInstallPlatform, m as wasAutoShown, n as VibesInstallPrompt, o as detectPlatform, s as isDismissed, u as isStandalone } from "./react-BHavFwjG.mjs";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import { createRoot } from "react-dom/client";
|
|
4
4
|
//#region src/auto.tsx
|
|
@@ -8,7 +8,7 @@ function shouldAutoShow() {
|
|
|
8
8
|
if (isStandalone()) return false;
|
|
9
9
|
if (isDismissed() || wasAutoShown()) return false;
|
|
10
10
|
const platform = detectPlatform();
|
|
11
|
-
if (platform
|
|
11
|
+
if (!isIosInstallPlatform(platform) && platform !== "android") return false;
|
|
12
12
|
return countVisit() >= 2;
|
|
13
13
|
}
|
|
14
14
|
function mount() {
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as countVisit, c as isInIframe,
|
|
2
|
-
export { InstallGuide, VibesInstallPrompt, countVisit, defaultAppName, detectPlatform, isDismissed, isInIframe, isStandalone, promptNativeInstall, setDismissed, useInstallPrompt };
|
|
1
|
+
import { a as countVisit, c as isInIframe, d as promptNativeInstall, i as useInstallPrompt, l as isIosInstallPlatform, n as VibesInstallPrompt, o as detectPlatform, p as setDismissed, r as defaultAppName, s as isDismissed, t as InstallGuide, u as isStandalone } from "./react-BHavFwjG.mjs";
|
|
2
|
+
export { InstallGuide, VibesInstallPrompt, countVisit, defaultAppName, detectPlatform, isDismissed, isInIframe, isIosInstallPlatform, isStandalone, promptNativeInstall, setDismissed, useInstallPrompt };
|
|
@@ -2,6 +2,9 @@ import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from
|
|
|
2
2
|
import { createPortal } from "react-dom";
|
|
3
3
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
4
|
//#region src/core.ts
|
|
5
|
+
function isIosInstallPlatform(platform) {
|
|
6
|
+
return platform === "ios-safari" || platform === "ios-chrome" || platform === "ios-other";
|
|
7
|
+
}
|
|
5
8
|
function detectPlatform() {
|
|
6
9
|
if (typeof navigator === "undefined") return "unsupported";
|
|
7
10
|
const ua = navigator.userAgent;
|
|
@@ -198,6 +201,7 @@ const CSS = `
|
|
|
198
201
|
.vpwa-open .vpwa-backdrop { opacity: 1; }
|
|
199
202
|
.vpwa-sheet {
|
|
200
203
|
position: relative; width: 100%; max-width: 430px;
|
|
204
|
+
max-height: calc(100dvh - 12px); overflow-y: auto; overscroll-behavior: contain;
|
|
201
205
|
background: var(--card, var(--vpwa-bg));
|
|
202
206
|
color: var(--card-foreground, var(--vpwa-fg));
|
|
203
207
|
border: 1px solid var(--border, var(--vpwa-border)); border-bottom: none;
|
|
@@ -253,6 +257,46 @@ const CSS = `
|
|
|
253
257
|
.vpwa-steptext { font-size: 14px; font-weight: 500; }
|
|
254
258
|
.vpwa-stepcap { font-weight: 400; color: var(--muted-foreground, var(--vpwa-muted)); }
|
|
255
259
|
|
|
260
|
+
/* ── iOS walkthrough video ──────────────────────────────────────────── */
|
|
261
|
+
.vpwa-video-guide {
|
|
262
|
+
display: flex; flex-direction: column; align-items: center; gap: 10px;
|
|
263
|
+
margin: 0; padding: 0;
|
|
264
|
+
}
|
|
265
|
+
.vpwa-video-frame {
|
|
266
|
+
position: relative; overflow: hidden;
|
|
267
|
+
width: min(100%, 31.5dvh, 292.5px);
|
|
268
|
+
aspect-ratio: 9 / 16;
|
|
269
|
+
border: 1px solid var(--border, var(--vpwa-border)); border-radius: 18px;
|
|
270
|
+
background: #000;
|
|
271
|
+
box-shadow: 0 14px 38px rgba(0, 0, 0, 0.24);
|
|
272
|
+
}
|
|
273
|
+
.vpwa-video {
|
|
274
|
+
display: block; width: 100%; height: 100%;
|
|
275
|
+
object-fit: cover;
|
|
276
|
+
}
|
|
277
|
+
.vpwa-video-loading {
|
|
278
|
+
position: absolute; inset: 0;
|
|
279
|
+
display: flex; align-items: flex-end; justify-content: center;
|
|
280
|
+
padding: 20px;
|
|
281
|
+
background: linear-gradient(to top, rgba(0, 0, 0, 0.48), transparent 42%);
|
|
282
|
+
pointer-events: none;
|
|
283
|
+
}
|
|
284
|
+
.vpwa-video-loading span {
|
|
285
|
+
color: transparent; font-size: 12px; font-weight: 600; line-height: 1;
|
|
286
|
+
letter-spacing: 0.01em;
|
|
287
|
+
background: linear-gradient(100deg, rgba(255,255,255,0.46) 20%, #fff 45%, rgba(255,255,255,0.46) 70%);
|
|
288
|
+
background-size: 220% 100%; background-clip: text; -webkit-background-clip: text;
|
|
289
|
+
animation: vpwa-text-shimmer 1.6s ease-in-out infinite;
|
|
290
|
+
}
|
|
291
|
+
@keyframes vpwa-text-shimmer {
|
|
292
|
+
from { background-position: 100% 0; }
|
|
293
|
+
to { background-position: -100% 0; }
|
|
294
|
+
}
|
|
295
|
+
.vpwa-video-guide figcaption {
|
|
296
|
+
max-width: 32rem; color: var(--muted-foreground, var(--vpwa-muted));
|
|
297
|
+
font-size: 12px; line-height: 1.45; text-align: center;
|
|
298
|
+
}
|
|
299
|
+
|
|
256
300
|
/* ── mocks ───────────────────────────────────────────────────────────── */
|
|
257
301
|
.vpwa-toolbar {
|
|
258
302
|
display: flex; align-items: center; justify-content: space-between; gap: 16px;
|
|
@@ -293,6 +337,7 @@ const CSS = `
|
|
|
293
337
|
}
|
|
294
338
|
@media (prefers-reduced-motion: reduce) {
|
|
295
339
|
.vpwa-target { animation: none; }
|
|
340
|
+
.vpwa-video-loading span { animation: none; color: rgba(255,255,255,0.82); }
|
|
296
341
|
.vpwa-sheet, .vpwa-backdrop, .vpwa-banner { transition: none; }
|
|
297
342
|
}
|
|
298
343
|
|
|
@@ -690,15 +735,19 @@ function stepsFor(platform) {
|
|
|
690
735
|
case "unsupported": return null;
|
|
691
736
|
}
|
|
692
737
|
}
|
|
693
|
-
function InstallGuide({ open, onClose, appName }) {
|
|
738
|
+
function InstallGuide({ open, onClose, appName, iosVideoSrc, iosVideoPoster }) {
|
|
694
739
|
const { platform, canPrompt, installed, promptInstall } = useInstallPrompt();
|
|
695
740
|
const name = appName ?? defaultAppName();
|
|
696
741
|
const [mounted, setMounted] = useState(open);
|
|
697
742
|
const [shown, setShown] = useState(false);
|
|
743
|
+
const [videoFailed, setVideoFailed] = useState(false);
|
|
744
|
+
const [videoLoading, setVideoLoading] = useState(true);
|
|
698
745
|
useEffect(() => {
|
|
699
746
|
ensureStyles();
|
|
700
747
|
if (open) {
|
|
701
748
|
setMounted(true);
|
|
749
|
+
setVideoFailed(false);
|
|
750
|
+
setVideoLoading(true);
|
|
702
751
|
const raf = requestAnimationFrame(() => requestAnimationFrame(() => setShown(true)));
|
|
703
752
|
return () => cancelAnimationFrame(raf);
|
|
704
753
|
}
|
|
@@ -746,6 +795,33 @@ function InstallGuide({ open, onClose, appName }) {
|
|
|
746
795
|
children: "Not now"
|
|
747
796
|
})]
|
|
748
797
|
});
|
|
798
|
+
else if (isIosInstallPlatform(platform) && iosVideoSrc && !videoFailed) body = /* @__PURE__ */ jsxs("figure", {
|
|
799
|
+
className: "vpwa-video-guide",
|
|
800
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
801
|
+
className: "vpwa-video-frame",
|
|
802
|
+
children: [/* @__PURE__ */ jsx("video", {
|
|
803
|
+
className: "vpwa-video",
|
|
804
|
+
src: iosVideoSrc,
|
|
805
|
+
poster: iosVideoPoster,
|
|
806
|
+
autoPlay: true,
|
|
807
|
+
disablePictureInPicture: true,
|
|
808
|
+
loop: true,
|
|
809
|
+
muted: true,
|
|
810
|
+
playsInline: true,
|
|
811
|
+
preload: "auto",
|
|
812
|
+
"aria-label": "iOS Add to Home Screen walkthrough",
|
|
813
|
+
onCanPlay: () => setVideoLoading(false),
|
|
814
|
+
onPlaying: () => setVideoLoading(false),
|
|
815
|
+
onWaiting: () => setVideoLoading(true),
|
|
816
|
+
onError: () => setVideoFailed(true)
|
|
817
|
+
}), videoLoading ? /* @__PURE__ */ jsx("div", {
|
|
818
|
+
className: "vpwa-video-loading",
|
|
819
|
+
role: "status",
|
|
820
|
+
"aria-label": "Loading walkthrough video",
|
|
821
|
+
children: /* @__PURE__ */ jsx("span", { children: "Loading walkthrough…" })
|
|
822
|
+
}) : null]
|
|
823
|
+
}), /* @__PURE__ */ jsx("figcaption", { children: "Open your browser’s Share menu, choose Add to Home Screen, then tap Add." })]
|
|
824
|
+
});
|
|
749
825
|
else if (steps) body = /* @__PURE__ */ jsx("ol", {
|
|
750
826
|
className: "vpwa-steps",
|
|
751
827
|
children: steps.map((step, i) => /* @__PURE__ */ jsxs("li", {
|
|
@@ -872,4 +948,4 @@ function VibesInstallPrompt({ appName, variant = "inline", className }) {
|
|
|
872
948
|
})] });
|
|
873
949
|
}
|
|
874
950
|
//#endregion
|
|
875
|
-
export { countVisit as a, isInIframe as c,
|
|
951
|
+
export { countVisit as a, isInIframe as c, promptNativeInstall as d, setAutoShown as f, useInstallPrompt as i, isIosInstallPlatform as l, wasAutoShown as m, VibesInstallPrompt as n, detectPlatform as o, setDismissed as p, defaultAppName as r, isDismissed as s, InstallGuide as t, isStandalone as u };
|
package/package.json
CHANGED
package/src/auto.tsx
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
detectPlatform,
|
|
19
19
|
isDismissed,
|
|
20
20
|
isInIframe,
|
|
21
|
+
isIosInstallPlatform,
|
|
21
22
|
isStandalone,
|
|
22
23
|
setAutoShown,
|
|
23
24
|
wasAutoShown,
|
|
@@ -30,7 +31,7 @@ function shouldAutoShow(): boolean {
|
|
|
30
31
|
if (isStandalone()) return false
|
|
31
32
|
if (isDismissed() || wasAutoShown()) return false
|
|
32
33
|
const platform = detectPlatform()
|
|
33
|
-
if (platform
|
|
34
|
+
if (!isIosInstallPlatform(platform) && platform !== "android") {
|
|
34
35
|
return false
|
|
35
36
|
}
|
|
36
37
|
return countVisit() >= 2
|
package/src/core.ts
CHANGED
|
@@ -20,6 +20,12 @@ export type InstallPlatform =
|
|
|
20
20
|
| "desktop-safari" // macOS Safari — File → Add to Dock
|
|
21
21
|
| "unsupported" // desktop Firefox, webviews, …
|
|
22
22
|
|
|
23
|
+
export function isIosInstallPlatform(
|
|
24
|
+
platform: InstallPlatform,
|
|
25
|
+
): platform is "ios-safari" | "ios-chrome" | "ios-other" {
|
|
26
|
+
return platform === "ios-safari" || platform === "ios-chrome" || platform === "ios-other"
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
export function detectPlatform(): InstallPlatform {
|
|
24
30
|
if (typeof navigator === "undefined") return "unsupported"
|
|
25
31
|
const ua = navigator.userAgent
|
package/src/index.ts
CHANGED
package/src/react.tsx
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
getServerInstallState,
|
|
14
14
|
isDismissed,
|
|
15
15
|
isInIframe,
|
|
16
|
+
isIosInstallPlatform,
|
|
16
17
|
promptNativeInstall,
|
|
17
18
|
setDismissed,
|
|
18
19
|
subscribeInstallState,
|
|
@@ -169,10 +170,16 @@ export function InstallGuide({
|
|
|
169
170
|
open,
|
|
170
171
|
onClose,
|
|
171
172
|
appName,
|
|
173
|
+
iosVideoSrc,
|
|
174
|
+
iosVideoPoster,
|
|
172
175
|
}: {
|
|
173
176
|
open: boolean
|
|
174
177
|
onClose: () => void
|
|
175
178
|
appName?: string
|
|
179
|
+
/** Optional iOS walkthrough video. Static steps remain the load-failure fallback. */
|
|
180
|
+
iosVideoSrc?: string
|
|
181
|
+
/** Optional poster shown while the iOS walkthrough video loads. */
|
|
182
|
+
iosVideoPoster?: string
|
|
176
183
|
}) {
|
|
177
184
|
const { platform, canPrompt, installed, promptInstall } = useInstallPrompt()
|
|
178
185
|
const name = appName ?? defaultAppName()
|
|
@@ -180,11 +187,15 @@ export function InstallGuide({
|
|
|
180
187
|
// `shown` drives the CSS open state one frame after mount.
|
|
181
188
|
const [mounted, setMounted] = useState(open)
|
|
182
189
|
const [shown, setShown] = useState(false)
|
|
190
|
+
const [videoFailed, setVideoFailed] = useState(false)
|
|
191
|
+
const [videoLoading, setVideoLoading] = useState(true)
|
|
183
192
|
|
|
184
193
|
useEffect(() => {
|
|
185
194
|
ensureStyles()
|
|
186
195
|
if (open) {
|
|
187
196
|
setMounted(true)
|
|
197
|
+
setVideoFailed(false)
|
|
198
|
+
setVideoLoading(true)
|
|
188
199
|
const raf = requestAnimationFrame(() => requestAnimationFrame(() => setShown(true)))
|
|
189
200
|
return () => cancelAnimationFrame(raf)
|
|
190
201
|
}
|
|
@@ -235,6 +246,37 @@ export function InstallGuide({
|
|
|
235
246
|
</button>
|
|
236
247
|
</div>
|
|
237
248
|
)
|
|
249
|
+
} else if (isIosInstallPlatform(platform) && iosVideoSrc && !videoFailed) {
|
|
250
|
+
body = (
|
|
251
|
+
<figure className="vpwa-video-guide">
|
|
252
|
+
<div className="vpwa-video-frame">
|
|
253
|
+
<video
|
|
254
|
+
className="vpwa-video"
|
|
255
|
+
src={iosVideoSrc}
|
|
256
|
+
poster={iosVideoPoster}
|
|
257
|
+
autoPlay
|
|
258
|
+
disablePictureInPicture
|
|
259
|
+
loop
|
|
260
|
+
muted
|
|
261
|
+
playsInline
|
|
262
|
+
preload="auto"
|
|
263
|
+
aria-label="iOS Add to Home Screen walkthrough"
|
|
264
|
+
onCanPlay={() => setVideoLoading(false)}
|
|
265
|
+
onPlaying={() => setVideoLoading(false)}
|
|
266
|
+
onWaiting={() => setVideoLoading(true)}
|
|
267
|
+
onError={() => setVideoFailed(true)}
|
|
268
|
+
/>
|
|
269
|
+
{videoLoading ? (
|
|
270
|
+
<div className="vpwa-video-loading" role="status" aria-label="Loading walkthrough video">
|
|
271
|
+
<span>Loading walkthrough…</span>
|
|
272
|
+
</div>
|
|
273
|
+
) : null}
|
|
274
|
+
</div>
|
|
275
|
+
<figcaption>
|
|
276
|
+
Open your browser’s Share menu, choose Add to Home Screen, then tap Add.
|
|
277
|
+
</figcaption>
|
|
278
|
+
</figure>
|
|
279
|
+
)
|
|
238
280
|
} else if (steps) {
|
|
239
281
|
body = (
|
|
240
282
|
<ol className="vpwa-steps">
|
package/src/styles.ts
CHANGED
|
@@ -54,6 +54,7 @@ const CSS = `
|
|
|
54
54
|
.vpwa-open .vpwa-backdrop { opacity: 1; }
|
|
55
55
|
.vpwa-sheet {
|
|
56
56
|
position: relative; width: 100%; max-width: 430px;
|
|
57
|
+
max-height: calc(100dvh - 12px); overflow-y: auto; overscroll-behavior: contain;
|
|
57
58
|
background: var(--card, var(--vpwa-bg));
|
|
58
59
|
color: var(--card-foreground, var(--vpwa-fg));
|
|
59
60
|
border: 1px solid var(--border, var(--vpwa-border)); border-bottom: none;
|
|
@@ -109,6 +110,46 @@ const CSS = `
|
|
|
109
110
|
.vpwa-steptext { font-size: 14px; font-weight: 500; }
|
|
110
111
|
.vpwa-stepcap { font-weight: 400; color: var(--muted-foreground, var(--vpwa-muted)); }
|
|
111
112
|
|
|
113
|
+
/* ── iOS walkthrough video ──────────────────────────────────────────── */
|
|
114
|
+
.vpwa-video-guide {
|
|
115
|
+
display: flex; flex-direction: column; align-items: center; gap: 10px;
|
|
116
|
+
margin: 0; padding: 0;
|
|
117
|
+
}
|
|
118
|
+
.vpwa-video-frame {
|
|
119
|
+
position: relative; overflow: hidden;
|
|
120
|
+
width: min(100%, 31.5dvh, 292.5px);
|
|
121
|
+
aspect-ratio: 9 / 16;
|
|
122
|
+
border: 1px solid var(--border, var(--vpwa-border)); border-radius: 18px;
|
|
123
|
+
background: #000;
|
|
124
|
+
box-shadow: 0 14px 38px rgba(0, 0, 0, 0.24);
|
|
125
|
+
}
|
|
126
|
+
.vpwa-video {
|
|
127
|
+
display: block; width: 100%; height: 100%;
|
|
128
|
+
object-fit: cover;
|
|
129
|
+
}
|
|
130
|
+
.vpwa-video-loading {
|
|
131
|
+
position: absolute; inset: 0;
|
|
132
|
+
display: flex; align-items: flex-end; justify-content: center;
|
|
133
|
+
padding: 20px;
|
|
134
|
+
background: linear-gradient(to top, rgba(0, 0, 0, 0.48), transparent 42%);
|
|
135
|
+
pointer-events: none;
|
|
136
|
+
}
|
|
137
|
+
.vpwa-video-loading span {
|
|
138
|
+
color: transparent; font-size: 12px; font-weight: 600; line-height: 1;
|
|
139
|
+
letter-spacing: 0.01em;
|
|
140
|
+
background: linear-gradient(100deg, rgba(255,255,255,0.46) 20%, #fff 45%, rgba(255,255,255,0.46) 70%);
|
|
141
|
+
background-size: 220% 100%; background-clip: text; -webkit-background-clip: text;
|
|
142
|
+
animation: vpwa-text-shimmer 1.6s ease-in-out infinite;
|
|
143
|
+
}
|
|
144
|
+
@keyframes vpwa-text-shimmer {
|
|
145
|
+
from { background-position: 100% 0; }
|
|
146
|
+
to { background-position: -100% 0; }
|
|
147
|
+
}
|
|
148
|
+
.vpwa-video-guide figcaption {
|
|
149
|
+
max-width: 32rem; color: var(--muted-foreground, var(--vpwa-muted));
|
|
150
|
+
font-size: 12px; line-height: 1.45; text-align: center;
|
|
151
|
+
}
|
|
152
|
+
|
|
112
153
|
/* ── mocks ───────────────────────────────────────────────────────────── */
|
|
113
154
|
.vpwa-toolbar {
|
|
114
155
|
display: flex; align-items: center; justify-content: space-between; gap: 16px;
|
|
@@ -149,6 +190,7 @@ const CSS = `
|
|
|
149
190
|
}
|
|
150
191
|
@media (prefers-reduced-motion: reduce) {
|
|
151
192
|
.vpwa-target { animation: none; }
|
|
193
|
+
.vpwa-video-loading span { animation: none; color: rgba(255,255,255,0.82); }
|
|
152
194
|
.vpwa-sheet, .vpwa-backdrop, .vpwa-banner { transition: none; }
|
|
153
195
|
}
|
|
154
196
|
|