@omg-dev/pwa 0.4.33 → 0.4.34
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-FTnyFbZ_.mjs} +42 -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 +28 -0
- package/src/styles.ts +18 -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-FTnyFbZ_.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-FTnyFbZ_.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,23 @@ 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 {
|
|
266
|
+
display: block; width: auto; max-width: 100%;
|
|
267
|
+
height: min(56dvh, 520px); max-height: 520px;
|
|
268
|
+
border: 1px solid var(--border, var(--vpwa-border)); border-radius: 18px;
|
|
269
|
+
background: #000; object-fit: contain;
|
|
270
|
+
box-shadow: 0 14px 38px rgba(0, 0, 0, 0.24);
|
|
271
|
+
}
|
|
272
|
+
.vpwa-video-guide figcaption {
|
|
273
|
+
max-width: 32rem; color: var(--muted-foreground, var(--vpwa-muted));
|
|
274
|
+
font-size: 12px; line-height: 1.45; text-align: center;
|
|
275
|
+
}
|
|
276
|
+
|
|
256
277
|
/* ── mocks ───────────────────────────────────────────────────────────── */
|
|
257
278
|
.vpwa-toolbar {
|
|
258
279
|
display: flex; align-items: center; justify-content: space-between; gap: 16px;
|
|
@@ -690,15 +711,17 @@ function stepsFor(platform) {
|
|
|
690
711
|
case "unsupported": return null;
|
|
691
712
|
}
|
|
692
713
|
}
|
|
693
|
-
function InstallGuide({ open, onClose, appName }) {
|
|
714
|
+
function InstallGuide({ open, onClose, appName, iosVideoSrc }) {
|
|
694
715
|
const { platform, canPrompt, installed, promptInstall } = useInstallPrompt();
|
|
695
716
|
const name = appName ?? defaultAppName();
|
|
696
717
|
const [mounted, setMounted] = useState(open);
|
|
697
718
|
const [shown, setShown] = useState(false);
|
|
719
|
+
const [videoFailed, setVideoFailed] = useState(false);
|
|
698
720
|
useEffect(() => {
|
|
699
721
|
ensureStyles();
|
|
700
722
|
if (open) {
|
|
701
723
|
setMounted(true);
|
|
724
|
+
setVideoFailed(false);
|
|
702
725
|
const raf = requestAnimationFrame(() => requestAnimationFrame(() => setShown(true)));
|
|
703
726
|
return () => cancelAnimationFrame(raf);
|
|
704
727
|
}
|
|
@@ -746,6 +769,23 @@ function InstallGuide({ open, onClose, appName }) {
|
|
|
746
769
|
children: "Not now"
|
|
747
770
|
})]
|
|
748
771
|
});
|
|
772
|
+
else if (isIosInstallPlatform(platform) && iosVideoSrc && !videoFailed) body = /* @__PURE__ */ jsxs("figure", {
|
|
773
|
+
className: "vpwa-video-guide",
|
|
774
|
+
children: [/* @__PURE__ */ jsx("video", {
|
|
775
|
+
className: "vpwa-video",
|
|
776
|
+
src: iosVideoSrc,
|
|
777
|
+
autoPlay: true,
|
|
778
|
+
controls: true,
|
|
779
|
+
controlsList: "nodownload noremoteplayback",
|
|
780
|
+
disablePictureInPicture: true,
|
|
781
|
+
loop: true,
|
|
782
|
+
muted: true,
|
|
783
|
+
playsInline: true,
|
|
784
|
+
preload: "auto",
|
|
785
|
+
"aria-label": "iOS Add to Home Screen walkthrough",
|
|
786
|
+
onError: () => setVideoFailed(true)
|
|
787
|
+
}), /* @__PURE__ */ jsx("figcaption", { children: "Open your browser’s Share menu, choose Add to Home Screen, then tap Add." })]
|
|
788
|
+
});
|
|
749
789
|
else if (steps) body = /* @__PURE__ */ jsx("ol", {
|
|
750
790
|
className: "vpwa-steps",
|
|
751
791
|
children: steps.map((step, i) => /* @__PURE__ */ jsxs("li", {
|
|
@@ -872,4 +912,4 @@ function VibesInstallPrompt({ appName, variant = "inline", className }) {
|
|
|
872
912
|
})] });
|
|
873
913
|
}
|
|
874
914
|
//#endregion
|
|
875
|
-
export { countVisit as a, isInIframe as c,
|
|
915
|
+
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,13 @@ export function InstallGuide({
|
|
|
169
170
|
open,
|
|
170
171
|
onClose,
|
|
171
172
|
appName,
|
|
173
|
+
iosVideoSrc,
|
|
172
174
|
}: {
|
|
173
175
|
open: boolean
|
|
174
176
|
onClose: () => void
|
|
175
177
|
appName?: string
|
|
178
|
+
/** Optional iOS walkthrough video. Static steps remain the load-failure fallback. */
|
|
179
|
+
iosVideoSrc?: string
|
|
176
180
|
}) {
|
|
177
181
|
const { platform, canPrompt, installed, promptInstall } = useInstallPrompt()
|
|
178
182
|
const name = appName ?? defaultAppName()
|
|
@@ -180,11 +184,13 @@ export function InstallGuide({
|
|
|
180
184
|
// `shown` drives the CSS open state one frame after mount.
|
|
181
185
|
const [mounted, setMounted] = useState(open)
|
|
182
186
|
const [shown, setShown] = useState(false)
|
|
187
|
+
const [videoFailed, setVideoFailed] = useState(false)
|
|
183
188
|
|
|
184
189
|
useEffect(() => {
|
|
185
190
|
ensureStyles()
|
|
186
191
|
if (open) {
|
|
187
192
|
setMounted(true)
|
|
193
|
+
setVideoFailed(false)
|
|
188
194
|
const raf = requestAnimationFrame(() => requestAnimationFrame(() => setShown(true)))
|
|
189
195
|
return () => cancelAnimationFrame(raf)
|
|
190
196
|
}
|
|
@@ -235,6 +241,28 @@ export function InstallGuide({
|
|
|
235
241
|
</button>
|
|
236
242
|
</div>
|
|
237
243
|
)
|
|
244
|
+
} else if (isIosInstallPlatform(platform) && iosVideoSrc && !videoFailed) {
|
|
245
|
+
body = (
|
|
246
|
+
<figure className="vpwa-video-guide">
|
|
247
|
+
<video
|
|
248
|
+
className="vpwa-video"
|
|
249
|
+
src={iosVideoSrc}
|
|
250
|
+
autoPlay
|
|
251
|
+
controls
|
|
252
|
+
controlsList="nodownload noremoteplayback"
|
|
253
|
+
disablePictureInPicture
|
|
254
|
+
loop
|
|
255
|
+
muted
|
|
256
|
+
playsInline
|
|
257
|
+
preload="auto"
|
|
258
|
+
aria-label="iOS Add to Home Screen walkthrough"
|
|
259
|
+
onError={() => setVideoFailed(true)}
|
|
260
|
+
/>
|
|
261
|
+
<figcaption>
|
|
262
|
+
Open your browser’s Share menu, choose Add to Home Screen, then tap Add.
|
|
263
|
+
</figcaption>
|
|
264
|
+
</figure>
|
|
265
|
+
)
|
|
238
266
|
} else if (steps) {
|
|
239
267
|
body = (
|
|
240
268
|
<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,23 @@ 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 {
|
|
119
|
+
display: block; width: auto; max-width: 100%;
|
|
120
|
+
height: min(56dvh, 520px); max-height: 520px;
|
|
121
|
+
border: 1px solid var(--border, var(--vpwa-border)); border-radius: 18px;
|
|
122
|
+
background: #000; object-fit: contain;
|
|
123
|
+
box-shadow: 0 14px 38px rgba(0, 0, 0, 0.24);
|
|
124
|
+
}
|
|
125
|
+
.vpwa-video-guide figcaption {
|
|
126
|
+
max-width: 32rem; color: var(--muted-foreground, var(--vpwa-muted));
|
|
127
|
+
font-size: 12px; line-height: 1.45; text-align: center;
|
|
128
|
+
}
|
|
129
|
+
|
|
112
130
|
/* ── mocks ───────────────────────────────────────────────────────────── */
|
|
113
131
|
.vpwa-toolbar {
|
|
114
132
|
display: flex; align-items: center; justify-content: space-between; gap: 16px;
|