offline-page-kit 0.2.2 → 0.3.3
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/README.md +56 -7
- package/dist/cli.cjs +400 -17
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +400 -17
- package/dist/cli.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -40,8 +40,8 @@
|
|
|
40
40
|
<a href="https://bundlephobia.com/package/offline-page-kit">
|
|
41
41
|
<img src="https://img.shields.io/bundlephobia/minzip/offline-page-kit?style=for-the-badge&color=purple" alt="bundle size">
|
|
42
42
|
</a>
|
|
43
|
-
<img src="https://img.shields.io/github/actions/workflow/status/
|
|
44
|
-
<img src="https://img.shields.io/codecov/c/github/
|
|
43
|
+
<img src="https://img.shields.io/github/actions/workflow/status/kuhelahmed2024/offline-page-kit/ci.yml?style=for-the-badge" alt="CI status">
|
|
44
|
+
<img src="https://img.shields.io/codecov/c/github/kuhelahmed2024/offline-page-kit?style=for-the-badge" alt="coverage">
|
|
45
45
|
<img src="https://img.shields.io/badge/TypeScript-Ready-3178c6?style=for-the-badge&logo=typescript&logoColor=white" alt="TypeScript Ready">
|
|
46
46
|
<img src="https://img.shields.io/badge/Zero-Dependencies-black?style=for-the-badge" alt="Zero Dependencies">
|
|
47
47
|
</p>
|
|
@@ -77,6 +77,18 @@ Just reliable navigation fallback.
|
|
|
77
77
|
|
|
78
78
|
---
|
|
79
79
|
|
|
80
|
+
# 🟣 Demo
|
|
81
|
+
|
|
82
|
+
<p align="center">
|
|
83
|
+
<img src="./img/example.png" width="600" alt="demo gif placeholder">
|
|
84
|
+
</p>
|
|
85
|
+
|
|
86
|
+
> - Online page load
|
|
87
|
+
> - DevTools → Offline mode
|
|
88
|
+
> - Refresh → Offline page appears
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
80
92
|
# 🚀 Quick Start
|
|
81
93
|
|
|
82
94
|
### 1️⃣ Install
|
|
@@ -156,17 +168,32 @@ Install → Activate → Control → Fetch
|
|
|
156
168
|
## Next.js (App Router)
|
|
157
169
|
|
|
158
170
|
```tsx
|
|
171
|
+
|
|
172
|
+
// #/app/page.tsx
|
|
159
173
|
"use client";
|
|
160
174
|
|
|
161
175
|
import { useEffect } from "react";
|
|
162
176
|
import { registerOfflineKit } from "offline-page-kit";
|
|
163
177
|
|
|
164
|
-
export default function
|
|
178
|
+
export default function Home() {
|
|
165
179
|
useEffect(() => {
|
|
166
|
-
registerOfflineKit(
|
|
180
|
+
registerOfflineKit(
|
|
181
|
+
{
|
|
182
|
+
swUrl: "/sw.js",
|
|
183
|
+
scope: "/",
|
|
184
|
+
debug: true
|
|
185
|
+
}
|
|
186
|
+
);
|
|
167
187
|
}, []);
|
|
168
|
-
return
|
|
188
|
+
return (
|
|
189
|
+
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
|
190
|
+
<h1 className="text-4xl font-bold">You are Online.</h1>
|
|
191
|
+
</main>
|
|
192
|
+
);
|
|
169
193
|
}
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
|
|
170
197
|
```
|
|
171
198
|
|
|
172
199
|
---
|
|
@@ -181,14 +208,36 @@ registerOfflineKit();
|
|
|
181
208
|
|
|
182
209
|
---
|
|
183
210
|
|
|
184
|
-
# ⚙
|
|
211
|
+
# ⚙ Windows PowerShell (VSCode)
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
npx offline-page-kit init `
|
|
215
|
+
--outDir public `
|
|
216
|
+
--offlinePage /offline.html `
|
|
217
|
+
--offlineImage /offline.svg `
|
|
218
|
+
--cacheName my-app-cache-v1
|
|
219
|
+
```
|
|
220
|
+
---
|
|
221
|
+
|
|
222
|
+
# 🔹⚙ Windows CMD
|
|
223
|
+
|
|
224
|
+
```
|
|
225
|
+
npx offline-page-kit init ^
|
|
226
|
+
--outDir public ^
|
|
227
|
+
--offlinePage /offline.html ^
|
|
228
|
+
--offlineImage /offline.svg ^
|
|
229
|
+
--cacheName my-app-cache-v1
|
|
230
|
+
```
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
# ⚙ Linux/macOS (Bash/Zsh)
|
|
185
234
|
|
|
186
235
|
```
|
|
187
236
|
npx offline-page-kit init \
|
|
188
237
|
--outDir public \
|
|
189
238
|
--offlinePage /offline.html \
|
|
190
239
|
--offlineImage /offline.svg \
|
|
191
|
-
--cacheName my-app-cache
|
|
240
|
+
--cacheName my-app-cache-v1
|
|
192
241
|
```
|
|
193
242
|
|
|
194
243
|
| Option | Default | Description |
|
package/dist/cli.cjs
CHANGED
|
@@ -46,14 +46,20 @@ function exists(filePath) {
|
|
|
46
46
|
}
|
|
47
47
|
function parseArgs(argv) {
|
|
48
48
|
const m = /* @__PURE__ */ new Map();
|
|
49
|
+
const norm = (k) => k.replace(/-/g, "").toLowerCase();
|
|
49
50
|
for (let i = 0; i < argv.length; i++) {
|
|
50
51
|
const a = argv[i];
|
|
51
52
|
if (!a.startsWith("--")) continue;
|
|
52
|
-
const
|
|
53
|
+
const rawKey = a.slice(2);
|
|
54
|
+
const key = norm(rawKey);
|
|
53
55
|
const value = argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[++i] : "true";
|
|
54
56
|
m.set(key, value);
|
|
55
57
|
}
|
|
56
|
-
return
|
|
58
|
+
return {
|
|
59
|
+
get(name) {
|
|
60
|
+
return m.get(norm(name));
|
|
61
|
+
}
|
|
62
|
+
};
|
|
57
63
|
}
|
|
58
64
|
function splitList(v) {
|
|
59
65
|
return (v || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -67,26 +73,403 @@ function offlineHtmlTemplate(title = "You're Offline") {
|
|
|
67
73
|
<meta charset="UTF-8"/>
|
|
68
74
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
69
75
|
<title>${title}</title>
|
|
76
|
+
<meta name="theme-color" content="#0b0f19"/>
|
|
77
|
+
|
|
70
78
|
<style>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
+
:root{
|
|
80
|
+
--bg0:#060912;
|
|
81
|
+
--bg1:#0b1022;
|
|
82
|
+
--card: rgba(255,255,255,.06);
|
|
83
|
+
--card2: rgba(255,255,255,.04);
|
|
84
|
+
--stroke: rgba(255,255,255,.14);
|
|
85
|
+
--text:#ffffff;
|
|
86
|
+
--muted: rgba(255,255,255,.78);
|
|
87
|
+
--muted2: rgba(255,255,255,.62);
|
|
88
|
+
--good:#2ee59d;
|
|
89
|
+
--warn:#ffd166;
|
|
90
|
+
--shadow: 0 20px 80px rgba(0,0,0,.55);
|
|
91
|
+
--radius: 22px;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
*{box-sizing:border-box}
|
|
95
|
+
html,body{height:100%}
|
|
96
|
+
body{
|
|
97
|
+
margin:0;
|
|
98
|
+
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji";
|
|
99
|
+
color:var(--text);
|
|
100
|
+
background:
|
|
101
|
+
radial-gradient(900px 500px at 18% 18%, rgba(120,140,255,.20), transparent 60%),
|
|
102
|
+
radial-gradient(700px 500px at 82% 20%, rgba(46,229,157,.16), transparent 58%),
|
|
103
|
+
radial-gradient(600px 520px at 50% 95%, rgba(255,209,102,.14), transparent 55%),
|
|
104
|
+
linear-gradient(180deg, var(--bg0), var(--bg1));
|
|
105
|
+
overflow:hidden;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/* Floating blobs (subtle animation) */
|
|
109
|
+
.blob{
|
|
110
|
+
position:absolute; inset:auto;
|
|
111
|
+
width: 520px; height: 520px;
|
|
112
|
+
border-radius: 50%;
|
|
113
|
+
filter: blur(42px);
|
|
114
|
+
opacity:.35;
|
|
115
|
+
animation: float 10s ease-in-out infinite;
|
|
116
|
+
pointer-events:none;
|
|
117
|
+
transform: translate3d(0,0,0);
|
|
118
|
+
}
|
|
119
|
+
.blob.b1{left:-180px; top:-200px; background: rgba(120,140,255,.55); animation-duration: 12s;}
|
|
120
|
+
.blob.b2{right:-210px; top:-150px; background: rgba(46,229,157,.52); animation-duration: 14s;}
|
|
121
|
+
.blob.b3{left:18%; bottom:-320px; background: rgba(255,209,102,.45); animation-duration: 16s;}
|
|
122
|
+
|
|
123
|
+
@keyframes float{
|
|
124
|
+
0%,100%{ transform: translate(0,0) scale(1);}
|
|
125
|
+
50%{ transform: translate(22px,-18px) scale(1.06);}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Main layout */
|
|
129
|
+
.wrap{
|
|
130
|
+
min-height:100%;
|
|
131
|
+
display:grid;
|
|
132
|
+
place-items:center;
|
|
133
|
+
padding: 24px;
|
|
134
|
+
position:relative;
|
|
135
|
+
z-index:1;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.card{
|
|
139
|
+
width:min(720px, 100%);
|
|
140
|
+
border-radius: var(--radius);
|
|
141
|
+
background: linear-gradient(180deg, var(--card), var(--card2));
|
|
142
|
+
border:1px solid var(--stroke);
|
|
143
|
+
box-shadow: var(--shadow);
|
|
144
|
+
padding: 26px;
|
|
145
|
+
backdrop-filter: blur(10px);
|
|
146
|
+
-webkit-backdrop-filter: blur(10px);
|
|
147
|
+
|
|
148
|
+
animation: pop .55s cubic-bezier(.2,.9,.2,1) both;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@keyframes pop{
|
|
152
|
+
from{ opacity:0; transform: translateY(14px) scale(.98); }
|
|
153
|
+
to{ opacity:1; transform: translateY(0) scale(1); }
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.top{
|
|
157
|
+
display:flex;
|
|
158
|
+
align-items:flex-start;
|
|
159
|
+
justify-content:space-between;
|
|
160
|
+
gap:16px;
|
|
161
|
+
margin-bottom: 14px;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.badge{
|
|
165
|
+
display:inline-flex;
|
|
166
|
+
align-items:center;
|
|
167
|
+
gap:10px;
|
|
168
|
+
padding: 10px 12px;
|
|
169
|
+
border-radius: 999px;
|
|
170
|
+
border:1px solid rgba(255,255,255,.14);
|
|
171
|
+
background: rgba(0,0,0,.18);
|
|
172
|
+
color: var(--muted);
|
|
173
|
+
font-size: 13px;
|
|
174
|
+
user-select:none;
|
|
175
|
+
white-space:nowrap;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* Ping dot */
|
|
179
|
+
.dot{
|
|
180
|
+
width:10px;height:10px;border-radius:50%;
|
|
181
|
+
background: var(--warn);
|
|
182
|
+
box-shadow: 0 0 0 0 rgba(255,209,102,.65);
|
|
183
|
+
animation: ping 1.6s infinite;
|
|
184
|
+
}
|
|
185
|
+
@keyframes ping{
|
|
186
|
+
0%{ box-shadow: 0 0 0 0 rgba(255,209,102,.55); }
|
|
187
|
+
70%{ box-shadow: 0 0 0 10px rgba(255,209,102,0); }
|
|
188
|
+
100%{ box-shadow: 0 0 0 0 rgba(255,209,102,0); }
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
h1{
|
|
192
|
+
margin: 0 0 8px;
|
|
193
|
+
font-size: clamp(26px, 3.3vw, 34px);
|
|
194
|
+
letter-spacing: -0.02em;
|
|
195
|
+
line-height: 1.15;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
p{
|
|
199
|
+
margin: 0;
|
|
200
|
+
color: var(--muted2);
|
|
201
|
+
line-height: 1.55;
|
|
202
|
+
font-size: 15.5px;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
.hero{
|
|
206
|
+
display:flex;
|
|
207
|
+
align-items:center;
|
|
208
|
+
gap: 14px;
|
|
209
|
+
margin-top: 10px;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Animated wifi icon */
|
|
213
|
+
.wifi{
|
|
214
|
+
width: 54px; height: 54px;
|
|
215
|
+
border-radius: 16px;
|
|
216
|
+
display:grid; place-items:center;
|
|
217
|
+
background: rgba(255,255,255,.06);
|
|
218
|
+
border:1px solid rgba(255,255,255,.12);
|
|
219
|
+
position:relative;
|
|
220
|
+
overflow:hidden;
|
|
221
|
+
}
|
|
222
|
+
.wifi::after{
|
|
223
|
+
content:"";
|
|
224
|
+
position:absolute; inset:-40%;
|
|
225
|
+
background: radial-gradient(circle at 30% 30%, rgba(255,255,255,.18), transparent 55%);
|
|
226
|
+
animation: sheen 2.8s ease-in-out infinite;
|
|
227
|
+
}
|
|
228
|
+
@keyframes sheen{
|
|
229
|
+
0%,100%{ transform: translate(-8px,-6px) rotate(0deg); opacity:.75;}
|
|
230
|
+
50%{ transform: translate(10px,8px) rotate(8deg); opacity:.9;}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.wifi svg{ position:relative; z-index:1; opacity:.95; }
|
|
234
|
+
.wifi path{ animation: wave 1.6s ease-in-out infinite; transform-origin:center; }
|
|
235
|
+
.wifi path:nth-child(1){ opacity:.35; animation-delay:.05s;}
|
|
236
|
+
.wifi path:nth-child(2){ opacity:.55; animation-delay:.12s;}
|
|
237
|
+
.wifi path:nth-child(3){ opacity:.75; animation-delay:.2s;}
|
|
238
|
+
.wifi circle{ opacity:.9; animation: blink 1.2s ease-in-out infinite; }
|
|
239
|
+
|
|
240
|
+
@keyframes wave{
|
|
241
|
+
0%,100%{ transform: scale(1); }
|
|
242
|
+
50%{ transform: scale(1.06); }
|
|
243
|
+
}
|
|
244
|
+
@keyframes blink{
|
|
245
|
+
0%,100%{ opacity:.9; }
|
|
246
|
+
50%{ opacity:.45; }
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.actions{
|
|
250
|
+
display:flex;
|
|
251
|
+
flex-wrap:wrap;
|
|
252
|
+
gap: 10px;
|
|
253
|
+
margin-top: 18px;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.btn{
|
|
257
|
+
appearance:none;
|
|
258
|
+
border:0;
|
|
259
|
+
border-radius: 14px;
|
|
260
|
+
padding: 11px 14px;
|
|
261
|
+
cursor:pointer;
|
|
262
|
+
text-decoration:none;
|
|
263
|
+
display:inline-flex;
|
|
264
|
+
align-items:center;
|
|
265
|
+
gap:10px;
|
|
266
|
+
font-weight: 650;
|
|
267
|
+
font-size: 14.5px;
|
|
268
|
+
letter-spacing: .01em;
|
|
269
|
+
transition: transform .12s ease, filter .12s ease, background .2s ease, border-color .2s ease;
|
|
270
|
+
user-select:none;
|
|
271
|
+
}
|
|
272
|
+
.btn:active{ transform: translateY(1px) scale(.99); }
|
|
273
|
+
|
|
274
|
+
.primary{
|
|
275
|
+
background: #ffffff;
|
|
276
|
+
color: #0b0f19;
|
|
277
|
+
box-shadow: 0 8px 24px rgba(255,255,255,.12);
|
|
278
|
+
}
|
|
279
|
+
.primary:hover{ filter: brightness(0.98); }
|
|
280
|
+
|
|
281
|
+
.ghost{
|
|
282
|
+
background: rgba(255,255,255,.08);
|
|
283
|
+
color: var(--text);
|
|
284
|
+
border: 1px solid rgba(255,255,255,.14);
|
|
285
|
+
}
|
|
286
|
+
.ghost:hover{
|
|
287
|
+
background: rgba(255,255,255,.10);
|
|
288
|
+
border-color: rgba(255,255,255,.18);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.hint{
|
|
292
|
+
margin-top: 14px;
|
|
293
|
+
display:flex;
|
|
294
|
+
gap:10px;
|
|
295
|
+
flex-wrap:wrap;
|
|
296
|
+
align-items:center;
|
|
297
|
+
color: var(--muted2);
|
|
298
|
+
font-size: 13px;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.kbd{
|
|
302
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
|
303
|
+
font-size: 12px;
|
|
304
|
+
padding: 4px 8px;
|
|
305
|
+
border-radius: 9px;
|
|
306
|
+
border:1px solid rgba(255,255,255,.14);
|
|
307
|
+
background: rgba(0,0,0,.20);
|
|
308
|
+
color: rgba(255,255,255,.82);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/* Toast */
|
|
312
|
+
.toast{
|
|
313
|
+
position: fixed;
|
|
314
|
+
left: 50%;
|
|
315
|
+
bottom: 18px;
|
|
316
|
+
transform: translateX(-50%);
|
|
317
|
+
padding: 10px 12px;
|
|
318
|
+
border-radius: 999px;
|
|
319
|
+
background: rgba(0,0,0,.40);
|
|
320
|
+
border:1px solid rgba(255,255,255,.14);
|
|
321
|
+
color: rgba(255,255,255,.86);
|
|
322
|
+
font-size: 13px;
|
|
323
|
+
display:flex;
|
|
324
|
+
gap:10px;
|
|
325
|
+
align-items:center;
|
|
326
|
+
backdrop-filter: blur(10px);
|
|
327
|
+
-webkit-backdrop-filter: blur(10px);
|
|
328
|
+
box-shadow: 0 10px 40px rgba(0,0,0,.35);
|
|
329
|
+
opacity:0;
|
|
330
|
+
pointer-events:none;
|
|
331
|
+
transition: opacity .22s ease, transform .22s ease;
|
|
332
|
+
}
|
|
333
|
+
.toast.show{
|
|
334
|
+
opacity:1;
|
|
335
|
+
transform: translateX(-50%) translateY(-2px);
|
|
336
|
+
}
|
|
337
|
+
.toast .okdot{
|
|
338
|
+
width:10px;height:10px;border-radius:50%;
|
|
339
|
+
background: var(--good);
|
|
340
|
+
box-shadow: 0 0 0 6px rgba(46,229,157,.10);
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/* Reduce motion */
|
|
344
|
+
@media (prefers-reduced-motion: reduce){
|
|
345
|
+
.blob, .dot, .wifi path, .wifi circle, .card{ animation:none !important; }
|
|
346
|
+
.toast{ transition:none; }
|
|
347
|
+
}
|
|
79
348
|
</style>
|
|
80
349
|
</head>
|
|
350
|
+
|
|
81
351
|
<body>
|
|
82
|
-
<div class="
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
352
|
+
<div class="blob b1"></div>
|
|
353
|
+
<div class="blob b2"></div>
|
|
354
|
+
<div class="blob b3"></div>
|
|
355
|
+
|
|
356
|
+
<main class="wrap" role="main">
|
|
357
|
+
<section class="card" aria-labelledby="offline-title">
|
|
358
|
+
<div class="top">
|
|
359
|
+
<div>
|
|
360
|
+
<h1 id="offline-title">\u{1F635}\u200D\u{1F4AB} You\u2019re Offline</h1>
|
|
361
|
+
<p>
|
|
362
|
+
No internet connection detected. Don\u2019t worry \u2014 you can retry now,
|
|
363
|
+
or go home if it\u2019s already cached. \u2728
|
|
364
|
+
</p>
|
|
365
|
+
</div>
|
|
366
|
+
|
|
367
|
+
<div class="badge" aria-live="polite">
|
|
368
|
+
<span class="dot" aria-hidden="true"></span>
|
|
369
|
+
<span id="netText">Offline mode</span>
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
372
|
+
|
|
373
|
+
<div class="hero">
|
|
374
|
+
<div class="wifi" aria-hidden="true">
|
|
375
|
+
<svg width="30" height="30" viewBox="0 0 24 24" fill="none">
|
|
376
|
+
<path d="M2.5 9.2C8.6 3.2 15.4 3.2 21.5 9.2" stroke="white" stroke-width="2" stroke-linecap="round"/>
|
|
377
|
+
<path d="M5.8 12.4C10.1 8.3 13.9 8.3 18.2 12.4" stroke="white" stroke-width="2" stroke-linecap="round"/>
|
|
378
|
+
<path d="M9.2 15.7C11.2 13.9 12.8 13.9 14.8 15.7" stroke="white" stroke-width="2" stroke-linecap="round"/>
|
|
379
|
+
<circle cx="12" cy="19" r="1.4" fill="white"/>
|
|
380
|
+
</svg>
|
|
381
|
+
</div>
|
|
382
|
+
|
|
383
|
+
<div>
|
|
384
|
+
<p style="margin:0 0 6px;color:rgba(255,255,255,.86);font-weight:650">
|
|
385
|
+
Tip: Your cached pages may still open \u26A1
|
|
386
|
+
</p>
|
|
387
|
+
<p style="margin:0;color:rgba(255,255,255,.62)">
|
|
388
|
+
We\u2019ll auto-refresh when you\u2019re back online \u2705
|
|
389
|
+
</p>
|
|
390
|
+
</div>
|
|
391
|
+
</div>
|
|
392
|
+
|
|
393
|
+
<div class="actions">
|
|
394
|
+
<button class="btn primary" id="retryBtn" type="button">
|
|
395
|
+
\u{1F504} Retry
|
|
396
|
+
</button>
|
|
397
|
+
<a class="btn ghost" href="/" rel="noopener">
|
|
398
|
+
\u{1F3E0} Go Home
|
|
399
|
+
</a>
|
|
400
|
+
<button class="btn ghost" id="copyBtn" type="button" title="Copy current URL">
|
|
401
|
+
\u{1F517} Copy URL
|
|
402
|
+
</button>
|
|
403
|
+
</div>
|
|
404
|
+
|
|
405
|
+
<div class="hint">
|
|
406
|
+
<span>Quick keys:</span>
|
|
407
|
+
<span class="kbd">R</span> Retry
|
|
408
|
+
<span class="kbd">H</span> Home
|
|
409
|
+
</div>
|
|
410
|
+
</section>
|
|
411
|
+
</main>
|
|
412
|
+
|
|
413
|
+
<div class="toast" id="toast" role="status" aria-live="polite">
|
|
414
|
+
<span class="okdot" aria-hidden="true"></span>
|
|
415
|
+
<span id="toastText">Back online \u2014 reloading\u2026</span>
|
|
89
416
|
</div>
|
|
417
|
+
|
|
418
|
+
<script>
|
|
419
|
+
const retryBtn = document.getElementById("retryBtn");
|
|
420
|
+
const copyBtn = document.getElementById("copyBtn");
|
|
421
|
+
const netText = document.getElementById("netText");
|
|
422
|
+
const toast = document.getElementById("toast");
|
|
423
|
+
const toastText= document.getElementById("toastText");
|
|
424
|
+
|
|
425
|
+
function showToast(msg){
|
|
426
|
+
toastText.textContent = msg;
|
|
427
|
+
toast.classList.add("show");
|
|
428
|
+
clearTimeout(showToast._t);
|
|
429
|
+
showToast._t = setTimeout(() => toast.classList.remove("show"), 2200);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function updateStatus(){
|
|
433
|
+
const online = navigator.onLine;
|
|
434
|
+
netText.textContent = online ? "Online" : "Offline mode";
|
|
435
|
+
// If it becomes online, reload after tiny delay (feels smoother)
|
|
436
|
+
if (online){
|
|
437
|
+
showToast("\u2705 Back online \u2014 reloading\u2026");
|
|
438
|
+
setTimeout(() => location.reload(), 650);
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
retryBtn.addEventListener("click", () => location.reload());
|
|
443
|
+
|
|
444
|
+
copyBtn.addEventListener("click", async () => {
|
|
445
|
+
try{
|
|
446
|
+
await navigator.clipboard.writeText(location.href);
|
|
447
|
+
showToast("\u{1F4CB} URL copied!");
|
|
448
|
+
}catch{
|
|
449
|
+
// fallback
|
|
450
|
+
const ta = document.createElement("textarea");
|
|
451
|
+
ta.value = location.href;
|
|
452
|
+
document.body.appendChild(ta);
|
|
453
|
+
ta.select();
|
|
454
|
+
document.execCommand("copy");
|
|
455
|
+
ta.remove();
|
|
456
|
+
showToast("\u{1F4CB} URL copied!");
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
window.addEventListener("online", updateStatus);
|
|
461
|
+
window.addEventListener("offline", updateStatus);
|
|
462
|
+
|
|
463
|
+
// Keyboard shortcuts
|
|
464
|
+
window.addEventListener("keydown", (e) => {
|
|
465
|
+
const k = (e.key || "").toLowerCase();
|
|
466
|
+
if (k === "r") location.reload();
|
|
467
|
+
if (k === "h") location.href = "/";
|
|
468
|
+
});
|
|
469
|
+
|
|
470
|
+
// initial
|
|
471
|
+
updateStatus();
|
|
472
|
+
</script>
|
|
90
473
|
</body>
|
|
91
474
|
</html>`;
|
|
92
475
|
}
|
package/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/core/utils.ts","../src/core/offlineHtml.ts","../src/core/offlineSvg.ts","../src/core/swTemplate.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport path from \"node:path\";\r\nimport { parseArgs, splitList, writeFileSafe, exists } from \"./core/utils\";\r\nimport { offlineHtmlTemplate } from \"./core/offlineHtml\";\r\nimport { offlineSvgTemplate } from \"./core/offlineSvg\";\r\nimport { buildServiceWorkerJS } from \"./core/swTemplate\";\r\nimport type { OfflineKitBuildOptions } from \"./types\";\r\n\r\nfunction normalizePublicPath(p: string) {\r\n if (!p.startsWith(\"/\")) return \"/\" + p;\r\n return p;\r\n}\r\n\r\nfunction withDefaults(o: OfflineKitBuildOptions): Required<OfflineKitBuildOptions> {\r\n return {\r\n outDir: o.outDir ?? \"public\",\r\n swFileName: o.swFileName ?? \"sw.js\",\r\n offlinePage: normalizePublicPath(o.offlinePage ?? \"/offline.html\"),\r\n offlineImage: normalizePublicPath(o.offlineImage ?? \"/offline.svg\"),\r\n cacheName: o.cacheName ?? \"offline-page-kit\",\r\n precache: o.precache ?? [],\r\n htmlStrategy: o.htmlStrategy ?? \"networkFirst\",\r\n assetStrategy: o.assetStrategy ?? \"staleWhileRevalidate\",\r\n imageStrategy: o.imageStrategy ?? \"cacheFirst\",\r\n assetExtensions: o.assetExtensions ?? [],\r\n apiPrefixes: o.apiPrefixes ?? [],\r\n };\r\n}\r\n\r\nconst args = parseArgs(process.argv.slice(2));\r\nconst cmd = process.argv.slice(2).find(a => !a.startsWith(\"--\")) || \"init\";\r\n\r\nconst outDir = path.resolve(process.cwd(), args.get(\"outDir\") || \"public\");\r\n\r\nconst options = withDefaults({\r\n outDir,\r\n swFileName: args.get(\"swFileName\") || \"sw.js\",\r\n offlinePage: args.get(\"offlinePage\") || \"/offline.html\",\r\n offlineImage: args.get(\"offlineImage\") || \"/offline.svg\",\r\n cacheName: args.get(\"cacheName\") || \"offline-page-kit\",\r\n precache: splitList(args.get(\"precache\")),\r\n htmlStrategy: (args.get(\"htmlStrategy\") as any) || \"networkFirst\",\r\n assetStrategy: (args.get(\"assetStrategy\") as any) || \"staleWhileRevalidate\",\r\n imageStrategy: (args.get(\"imageStrategy\") as any) || \"cacheFirst\",\r\n assetExtensions: splitList(args.get(\"assetExtensions\")),\r\n apiPrefixes: splitList(args.get(\"apiPrefixes\")),\r\n} as OfflineKitBuildOptions);\r\n\r\nconst swOut = path.join(outDir, options.swFileName);\r\nconst offlineHtmlOut = path.join(outDir, options.offlinePage.replace(/^\\//, \"\"));\r\nconst offlineSvgOut = path.join(outDir, options.offlineImage.replace(/^\\//, \"\"));\r\n\r\nif (cmd === \"init\" || cmd === \"build\") {\r\n // generate offline page if missing OR force (when build)\r\n if (cmd === \"build\" || !exists(offlineHtmlOut)) {\r\n writeFileSafe(offlineHtmlOut, offlineHtmlTemplate());\r\n }\r\n if (cmd === \"build\" || !exists(offlineSvgOut)) {\r\n writeFileSafe(offlineSvgOut, offlineSvgTemplate());\r\n }\r\n\r\n const sw = buildServiceWorkerJS(options);\r\n writeFileSafe(swOut, sw);\r\n\r\n console.log(`[offline-page-kit] Generated:\r\n- ${swOut}\r\n- ${offlineHtmlOut}\r\n- ${offlineSvgOut}\r\n`);\r\n} else {\r\n console.log(`[offline-page-kit] Unknown command: ${cmd}\r\nUse:\r\n offline-page-kit init --outDir public\r\n offline-page-kit build --outDir public\r\n`);\r\n}","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\n\r\nexport function ensureDir(p: string) {\r\n fs.mkdirSync(p, { recursive: true });\r\n}\r\n\r\nexport function writeFileSafe(filePath: string, content: string) {\r\n ensureDir(path.dirname(filePath));\r\n fs.writeFileSync(filePath, content, \"utf8\");\r\n}\r\n\r\nexport function exists(filePath: string) {\r\n try { fs.accessSync(filePath); return true; } catch { return false; }\r\n}\r\n\r\nexport function parseArgs(argv: string[]) {\r\n const m = new Map<string, string>();\r\n for (let i = 0; i < argv.length; i++) {\r\n const a = argv[i];\r\n if (!a.startsWith(\"--\")) continue;\r\n const key = a.slice(2);\r\n const value = argv[i + 1] && !argv[i + 1].startsWith(\"--\") ? argv[++i] : \"true\";\r\n m.set(key, value);\r\n }\r\n return m;\r\n}\r\n\r\nexport function splitList(v: string | undefined) {\r\n return (v || \"\")\r\n .split(\",\")\r\n .map(s => s.trim())\r\n .filter(Boolean);\r\n}","export function offlineHtmlTemplate(title = \"You're Offline\") {\r\n return `<!doctype html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"UTF-8\"/>\r\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>\r\n <title>${title}</title>\r\n <style>\r\n body{margin:0;height:100vh;display:grid;place-items:center;font-family:system-ui,-apple-system,Segoe UI,Roboto;background:#0b0f19;color:#fff}\r\n .box{max-width:560px;padding:28px 26px;border-radius:18px;border:1px solid rgba(255,255,255,.12);background:rgba(255,255,255,.04)}\r\n h1{margin:0 0 10px;font-size:28px}\r\n p{margin:0 0 18px;opacity:.85;line-height:1.5}\r\n .row{display:flex;gap:10px;flex-wrap:wrap}\r\n button,a{appearance:none;border:0;border-radius:12px;padding:10px 14px;cursor:pointer;text-decoration:none}\r\n button{background:#fff;color:#111}\r\n a{background:rgba(255,255,255,.1);color:#fff}\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"box\">\r\n <h1>⚡ You’re Offline</h1>\r\n <p>No internet connection detected. You can retry, or go back to the homepage (if cached).</p>\r\n <div class=\"row\">\r\n <button onclick=\"location.reload()\">Retry</button>\r\n <a href=\"/\">Go Home</a>\r\n </div>\r\n </div>\r\n</body>\r\n</html>`;\r\n}","export function offlineSvgTemplate() {\r\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"630\" viewBox=\"0 0 1200 630\">\r\n <rect width=\"1200\" height=\"630\" fill=\"#0b0f19\"/>\r\n <text x=\"80\" y=\"220\" fill=\"#ffffff\" font-size=\"64\" font-family=\"system-ui, -apple-system, Segoe UI, Roboto\">You’re Offline</text>\r\n <text x=\"80\" y=\"300\" fill=\"#cbd5e1\" font-size=\"28\" font-family=\"system-ui, -apple-system, Segoe UI, Roboto\">Please check your connection and try again.</text>\r\n <circle cx=\"1040\" cy=\"220\" r=\"90\" fill=\"rgba(255,255,255,0.08)\"/>\r\n <path d=\"M980 220c40-40 120-40 160 0\" stroke=\"#fff\" stroke-width=\"10\" fill=\"none\" opacity=\"0.6\"/>\r\n <path d=\"M1010 250c25-25 75-25 100 0\" stroke=\"#fff\" stroke-width=\"10\" fill=\"none\" opacity=\"0.6\"/>\r\n <circle cx=\"1060\" cy=\"290\" r=\"10\" fill=\"#fff\" opacity=\"0.7\"/>\r\n</svg>`;\r\n}","import type { OfflineKitBuildOptions } from \"../types\";\r\n\r\nconst js = (v: unknown) => JSON.stringify(v);\r\n\r\nexport function buildServiceWorkerJS(options: Required<OfflineKitBuildOptions>) {\r\n const { cacheName, offlinePage, offlineImage } = options;\r\n\r\n return `/* offline-page-kit service worker (minimal) */\r\nconst CACHE_NAME = ${js(cacheName)};\r\nconst OFFLINE_PAGE = ${js(offlinePage)};\r\nconst OFFLINE_IMAGE = ${js(offlineImage)};\r\n\r\nself.addEventListener(\"install\", (event) => {\r\n event.waitUntil((async () => {\r\n const cache = await caches.open(CACHE_NAME);\r\n\r\n // ✅ Do not let one 404 kill the install\r\n await Promise.allSettled([\r\n cache.add(OFFLINE_PAGE),\r\n cache.add(OFFLINE_IMAGE),\r\n ]);\r\n\r\n await self.skipWaiting();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"activate\", (event) => {\r\n event.waitUntil((async () => {\r\n await self.clients.claim();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"fetch\", (event) => {\r\n const req = event.request;\r\n\r\n // ✅ Offline fallback only for page navigations\r\n if (req.mode === \"navigate\") {\r\n event.respondWith((async () => {\r\n try {\r\n return await fetch(req);\r\n } catch {\r\n const cache = await caches.open(CACHE_NAME);\r\n return (await cache.match(OFFLINE_PAGE)) || new Response(\"Offline\", { status: 503 });\r\n }\r\n })());\r\n }\r\n});\r\n`;\r\n}"],"mappings":"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","names":["import_node_path","fs","path","options","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/core/utils.ts","../src/core/offlineHtml.ts","../src/core/offlineSvg.ts","../src/core/swTemplate.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport path from \"node:path\";\r\nimport { parseArgs, splitList, writeFileSafe, exists } from \"./core/utils\";\r\nimport { offlineHtmlTemplate } from \"./core/offlineHtml\";\r\nimport { offlineSvgTemplate } from \"./core/offlineSvg\";\r\nimport { buildServiceWorkerJS } from \"./core/swTemplate\";\r\nimport type { OfflineKitBuildOptions } from \"./types\";\r\n\r\nfunction normalizePublicPath(p: string) {\r\n if (!p.startsWith(\"/\")) return \"/\" + p;\r\n return p;\r\n}\r\n\r\nfunction withDefaults(o: OfflineKitBuildOptions): Required<OfflineKitBuildOptions> {\r\n return {\r\n outDir: o.outDir ?? \"public\",\r\n swFileName: o.swFileName ?? \"sw.js\",\r\n offlinePage: normalizePublicPath(o.offlinePage ?? \"/offline.html\"),\r\n offlineImage: normalizePublicPath(o.offlineImage ?? \"/offline.svg\"),\r\n cacheName: o.cacheName ?? \"offline-page-kit\",\r\n precache: o.precache ?? [],\r\n htmlStrategy: o.htmlStrategy ?? \"networkFirst\",\r\n assetStrategy: o.assetStrategy ?? \"staleWhileRevalidate\",\r\n imageStrategy: o.imageStrategy ?? \"cacheFirst\",\r\n assetExtensions: o.assetExtensions ?? [],\r\n apiPrefixes: o.apiPrefixes ?? [],\r\n };\r\n}\r\n\r\nconst args = parseArgs(process.argv.slice(2));\r\nconst cmd = process.argv.slice(2).find(a => !a.startsWith(\"--\")) || \"init\";\r\n\r\nconst outDir = path.resolve(process.cwd(), args.get(\"outDir\") || \"public\");\r\n\r\nconst options = withDefaults({\r\n outDir,\r\n swFileName: args.get(\"swFileName\") || \"sw.js\",\r\n offlinePage: args.get(\"offlinePage\") || \"/offline.html\",\r\n offlineImage: args.get(\"offlineImage\") || \"/offline.svg\",\r\n cacheName: args.get(\"cacheName\") || \"offline-page-kit\",\r\n precache: splitList(args.get(\"precache\")),\r\n htmlStrategy: (args.get(\"htmlStrategy\") as any) || \"networkFirst\",\r\n assetStrategy: (args.get(\"assetStrategy\") as any) || \"staleWhileRevalidate\",\r\n imageStrategy: (args.get(\"imageStrategy\") as any) || \"cacheFirst\",\r\n assetExtensions: splitList(args.get(\"assetExtensions\")),\r\n apiPrefixes: splitList(args.get(\"apiPrefixes\")),\r\n} as OfflineKitBuildOptions);\r\n\r\nconst swOut = path.join(outDir, options.swFileName);\r\nconst offlineHtmlOut = path.join(outDir, options.offlinePage.replace(/^\\//, \"\"));\r\nconst offlineSvgOut = path.join(outDir, options.offlineImage.replace(/^\\//, \"\"));\r\n\r\nif (cmd === \"init\" || cmd === \"build\") {\r\n // generate offline page if missing OR force (when build)\r\n if (cmd === \"build\" || !exists(offlineHtmlOut)) {\r\n writeFileSafe(offlineHtmlOut, offlineHtmlTemplate());\r\n }\r\n if (cmd === \"build\" || !exists(offlineSvgOut)) {\r\n writeFileSafe(offlineSvgOut, offlineSvgTemplate());\r\n }\r\n\r\n const sw = buildServiceWorkerJS(options);\r\n writeFileSafe(swOut, sw);\r\n\r\n console.log(`[offline-page-kit] Generated:\r\n- ${swOut}\r\n- ${offlineHtmlOut}\r\n- ${offlineSvgOut}\r\n`);\r\n} else {\r\n console.log(`[offline-page-kit] Unknown command: ${cmd}\r\nUse:\r\n offline-page-kit init --outDir public\r\n offline-page-kit build --outDir public\r\n`);\r\n}","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\n\r\nexport function ensureDir(p: string) {\r\n fs.mkdirSync(p, { recursive: true });\r\n}\r\n\r\nexport function writeFileSafe(filePath: string, content: string) {\r\n ensureDir(path.dirname(filePath));\r\n fs.writeFileSync(filePath, content, \"utf8\");\r\n}\r\n\r\nexport function exists(filePath: string) {\r\n try { fs.accessSync(filePath); return true; } catch { return false; }\r\n}\r\n\r\nexport function parseArgs(argv: string[]) {\r\n const m = new Map<string, string>();\r\n\r\n const norm = (k: string) => k.replace(/-/g, \"\").toLowerCase();\r\n\r\n for (let i = 0; i < argv.length; i++) {\r\n const a = argv[i];\r\n if (!a.startsWith(\"--\")) continue;\r\n\r\n const rawKey = a.slice(2);\r\n const key = norm(rawKey);\r\n\r\n const value = argv[i + 1] && !argv[i + 1].startsWith(\"--\") ? argv[++i] : \"true\";\r\n m.set(key, value);\r\n }\r\n\r\n return {\r\n get(name: string) {\r\n return m.get(norm(name));\r\n }\r\n } as unknown as Map<string, string>;\r\n}\r\n\r\nexport function splitList(v: string | undefined) {\r\n return (v || \"\")\r\n .split(\",\")\r\n .map(s => s.trim())\r\n .filter(Boolean);\r\n}","export function offlineHtmlTemplate(title = \"You're Offline\") {\r\n return `<!doctype html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"UTF-8\"/>\r\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>\r\n <title>${title}</title>\r\n <meta name=\"theme-color\" content=\"#0b0f19\"/>\r\n\r\n <style>\r\n :root{\r\n --bg0:#060912;\r\n --bg1:#0b1022;\r\n --card: rgba(255,255,255,.06);\r\n --card2: rgba(255,255,255,.04);\r\n --stroke: rgba(255,255,255,.14);\r\n --text:#ffffff;\r\n --muted: rgba(255,255,255,.78);\r\n --muted2: rgba(255,255,255,.62);\r\n --good:#2ee59d;\r\n --warn:#ffd166;\r\n --shadow: 0 20px 80px rgba(0,0,0,.55);\r\n --radius: 22px;\r\n }\r\n\r\n *{box-sizing:border-box}\r\n html,body{height:100%}\r\n body{\r\n margin:0;\r\n font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\",\"Segoe UI Emoji\";\r\n color:var(--text);\r\n background:\r\n radial-gradient(900px 500px at 18% 18%, rgba(120,140,255,.20), transparent 60%),\r\n radial-gradient(700px 500px at 82% 20%, rgba(46,229,157,.16), transparent 58%),\r\n radial-gradient(600px 520px at 50% 95%, rgba(255,209,102,.14), transparent 55%),\r\n linear-gradient(180deg, var(--bg0), var(--bg1));\r\n overflow:hidden;\r\n }\r\n\r\n /* Floating blobs (subtle animation) */\r\n .blob{\r\n position:absolute; inset:auto;\r\n width: 520px; height: 520px;\r\n border-radius: 50%;\r\n filter: blur(42px);\r\n opacity:.35;\r\n animation: float 10s ease-in-out infinite;\r\n pointer-events:none;\r\n transform: translate3d(0,0,0);\r\n }\r\n .blob.b1{left:-180px; top:-200px; background: rgba(120,140,255,.55); animation-duration: 12s;}\r\n .blob.b2{right:-210px; top:-150px; background: rgba(46,229,157,.52); animation-duration: 14s;}\r\n .blob.b3{left:18%; bottom:-320px; background: rgba(255,209,102,.45); animation-duration: 16s;}\r\n\r\n @keyframes float{\r\n 0%,100%{ transform: translate(0,0) scale(1);}\r\n 50%{ transform: translate(22px,-18px) scale(1.06);}\r\n }\r\n\r\n /* Main layout */\r\n .wrap{\r\n min-height:100%;\r\n display:grid;\r\n place-items:center;\r\n padding: 24px;\r\n position:relative;\r\n z-index:1;\r\n }\r\n\r\n .card{\r\n width:min(720px, 100%);\r\n border-radius: var(--radius);\r\n background: linear-gradient(180deg, var(--card), var(--card2));\r\n border:1px solid var(--stroke);\r\n box-shadow: var(--shadow);\r\n padding: 26px;\r\n backdrop-filter: blur(10px);\r\n -webkit-backdrop-filter: blur(10px);\r\n\r\n animation: pop .55s cubic-bezier(.2,.9,.2,1) both;\r\n }\r\n\r\n @keyframes pop{\r\n from{ opacity:0; transform: translateY(14px) scale(.98); }\r\n to{ opacity:1; transform: translateY(0) scale(1); }\r\n }\r\n\r\n .top{\r\n display:flex;\r\n align-items:flex-start;\r\n justify-content:space-between;\r\n gap:16px;\r\n margin-bottom: 14px;\r\n }\r\n\r\n .badge{\r\n display:inline-flex;\r\n align-items:center;\r\n gap:10px;\r\n padding: 10px 12px;\r\n border-radius: 999px;\r\n border:1px solid rgba(255,255,255,.14);\r\n background: rgba(0,0,0,.18);\r\n color: var(--muted);\r\n font-size: 13px;\r\n user-select:none;\r\n white-space:nowrap;\r\n }\r\n\r\n /* Ping dot */\r\n .dot{\r\n width:10px;height:10px;border-radius:50%;\r\n background: var(--warn);\r\n box-shadow: 0 0 0 0 rgba(255,209,102,.65);\r\n animation: ping 1.6s infinite;\r\n }\r\n @keyframes ping{\r\n 0%{ box-shadow: 0 0 0 0 rgba(255,209,102,.55); }\r\n 70%{ box-shadow: 0 0 0 10px rgba(255,209,102,0); }\r\n 100%{ box-shadow: 0 0 0 0 rgba(255,209,102,0); }\r\n }\r\n\r\n h1{\r\n margin: 0 0 8px;\r\n font-size: clamp(26px, 3.3vw, 34px);\r\n letter-spacing: -0.02em;\r\n line-height: 1.15;\r\n }\r\n\r\n p{\r\n margin: 0;\r\n color: var(--muted2);\r\n line-height: 1.55;\r\n font-size: 15.5px;\r\n }\r\n\r\n .hero{\r\n display:flex;\r\n align-items:center;\r\n gap: 14px;\r\n margin-top: 10px;\r\n }\r\n\r\n /* Animated wifi icon */\r\n .wifi{\r\n width: 54px; height: 54px;\r\n border-radius: 16px;\r\n display:grid; place-items:center;\r\n background: rgba(255,255,255,.06);\r\n border:1px solid rgba(255,255,255,.12);\r\n position:relative;\r\n overflow:hidden;\r\n }\r\n .wifi::after{\r\n content:\"\";\r\n position:absolute; inset:-40%;\r\n background: radial-gradient(circle at 30% 30%, rgba(255,255,255,.18), transparent 55%);\r\n animation: sheen 2.8s ease-in-out infinite;\r\n }\r\n @keyframes sheen{\r\n 0%,100%{ transform: translate(-8px,-6px) rotate(0deg); opacity:.75;}\r\n 50%{ transform: translate(10px,8px) rotate(8deg); opacity:.9;}\r\n }\r\n\r\n .wifi svg{ position:relative; z-index:1; opacity:.95; }\r\n .wifi path{ animation: wave 1.6s ease-in-out infinite; transform-origin:center; }\r\n .wifi path:nth-child(1){ opacity:.35; animation-delay:.05s;}\r\n .wifi path:nth-child(2){ opacity:.55; animation-delay:.12s;}\r\n .wifi path:nth-child(3){ opacity:.75; animation-delay:.2s;}\r\n .wifi circle{ opacity:.9; animation: blink 1.2s ease-in-out infinite; }\r\n\r\n @keyframes wave{\r\n 0%,100%{ transform: scale(1); }\r\n 50%{ transform: scale(1.06); }\r\n }\r\n @keyframes blink{\r\n 0%,100%{ opacity:.9; }\r\n 50%{ opacity:.45; }\r\n }\r\n\r\n .actions{\r\n display:flex;\r\n flex-wrap:wrap;\r\n gap: 10px;\r\n margin-top: 18px;\r\n }\r\n\r\n .btn{\r\n appearance:none;\r\n border:0;\r\n border-radius: 14px;\r\n padding: 11px 14px;\r\n cursor:pointer;\r\n text-decoration:none;\r\n display:inline-flex;\r\n align-items:center;\r\n gap:10px;\r\n font-weight: 650;\r\n font-size: 14.5px;\r\n letter-spacing: .01em;\r\n transition: transform .12s ease, filter .12s ease, background .2s ease, border-color .2s ease;\r\n user-select:none;\r\n }\r\n .btn:active{ transform: translateY(1px) scale(.99); }\r\n\r\n .primary{\r\n background: #ffffff;\r\n color: #0b0f19;\r\n box-shadow: 0 8px 24px rgba(255,255,255,.12);\r\n }\r\n .primary:hover{ filter: brightness(0.98); }\r\n\r\n .ghost{\r\n background: rgba(255,255,255,.08);\r\n color: var(--text);\r\n border: 1px solid rgba(255,255,255,.14);\r\n }\r\n .ghost:hover{\r\n background: rgba(255,255,255,.10);\r\n border-color: rgba(255,255,255,.18);\r\n }\r\n\r\n .hint{\r\n margin-top: 14px;\r\n display:flex;\r\n gap:10px;\r\n flex-wrap:wrap;\r\n align-items:center;\r\n color: var(--muted2);\r\n font-size: 13px;\r\n }\r\n\r\n .kbd{\r\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace;\r\n font-size: 12px;\r\n padding: 4px 8px;\r\n border-radius: 9px;\r\n border:1px solid rgba(255,255,255,.14);\r\n background: rgba(0,0,0,.20);\r\n color: rgba(255,255,255,.82);\r\n }\r\n\r\n /* Toast */\r\n .toast{\r\n position: fixed;\r\n left: 50%;\r\n bottom: 18px;\r\n transform: translateX(-50%);\r\n padding: 10px 12px;\r\n border-radius: 999px;\r\n background: rgba(0,0,0,.40);\r\n border:1px solid rgba(255,255,255,.14);\r\n color: rgba(255,255,255,.86);\r\n font-size: 13px;\r\n display:flex;\r\n gap:10px;\r\n align-items:center;\r\n backdrop-filter: blur(10px);\r\n -webkit-backdrop-filter: blur(10px);\r\n box-shadow: 0 10px 40px rgba(0,0,0,.35);\r\n opacity:0;\r\n pointer-events:none;\r\n transition: opacity .22s ease, transform .22s ease;\r\n }\r\n .toast.show{\r\n opacity:1;\r\n transform: translateX(-50%) translateY(-2px);\r\n }\r\n .toast .okdot{\r\n width:10px;height:10px;border-radius:50%;\r\n background: var(--good);\r\n box-shadow: 0 0 0 6px rgba(46,229,157,.10);\r\n }\r\n\r\n /* Reduce motion */\r\n @media (prefers-reduced-motion: reduce){\r\n .blob, .dot, .wifi path, .wifi circle, .card{ animation:none !important; }\r\n .toast{ transition:none; }\r\n }\r\n </style>\r\n</head>\r\n\r\n<body>\r\n <div class=\"blob b1\"></div>\r\n <div class=\"blob b2\"></div>\r\n <div class=\"blob b3\"></div>\r\n\r\n <main class=\"wrap\" role=\"main\">\r\n <section class=\"card\" aria-labelledby=\"offline-title\">\r\n <div class=\"top\">\r\n <div>\r\n <h1 id=\"offline-title\">😵💫 You’re Offline</h1>\r\n <p>\r\n No internet connection detected. Don’t worry — you can retry now,\r\n or go home if it’s already cached. ✨\r\n </p>\r\n </div>\r\n\r\n <div class=\"badge\" aria-live=\"polite\">\r\n <span class=\"dot\" aria-hidden=\"true\"></span>\r\n <span id=\"netText\">Offline mode</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"hero\">\r\n <div class=\"wifi\" aria-hidden=\"true\">\r\n <svg width=\"30\" height=\"30\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path d=\"M2.5 9.2C8.6 3.2 15.4 3.2 21.5 9.2\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n <path d=\"M5.8 12.4C10.1 8.3 13.9 8.3 18.2 12.4\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n <path d=\"M9.2 15.7C11.2 13.9 12.8 13.9 14.8 15.7\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n <circle cx=\"12\" cy=\"19\" r=\"1.4\" fill=\"white\"/>\r\n </svg>\r\n </div>\r\n\r\n <div>\r\n <p style=\"margin:0 0 6px;color:rgba(255,255,255,.86);font-weight:650\">\r\n Tip: Your cached pages may still open ⚡\r\n </p>\r\n <p style=\"margin:0;color:rgba(255,255,255,.62)\">\r\n We’ll auto-refresh when you’re back online ✅\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <div class=\"actions\">\r\n <button class=\"btn primary\" id=\"retryBtn\" type=\"button\">\r\n 🔄 Retry\r\n </button>\r\n <a class=\"btn ghost\" href=\"/\" rel=\"noopener\">\r\n 🏠 Go Home\r\n </a>\r\n <button class=\"btn ghost\" id=\"copyBtn\" type=\"button\" title=\"Copy current URL\">\r\n 🔗 Copy URL\r\n </button>\r\n </div>\r\n\r\n <div class=\"hint\">\r\n <span>Quick keys:</span>\r\n <span class=\"kbd\">R</span> Retry\r\n <span class=\"kbd\">H</span> Home\r\n </div>\r\n </section>\r\n </main>\r\n\r\n <div class=\"toast\" id=\"toast\" role=\"status\" aria-live=\"polite\">\r\n <span class=\"okdot\" aria-hidden=\"true\"></span>\r\n <span id=\"toastText\">Back online — reloading…</span>\r\n </div>\r\n\r\n <script>\r\n const retryBtn = document.getElementById(\"retryBtn\");\r\n const copyBtn = document.getElementById(\"copyBtn\");\r\n const netText = document.getElementById(\"netText\");\r\n const toast = document.getElementById(\"toast\");\r\n const toastText= document.getElementById(\"toastText\");\r\n\r\n function showToast(msg){\r\n toastText.textContent = msg;\r\n toast.classList.add(\"show\");\r\n clearTimeout(showToast._t);\r\n showToast._t = setTimeout(() => toast.classList.remove(\"show\"), 2200);\r\n }\r\n\r\n function updateStatus(){\r\n const online = navigator.onLine;\r\n netText.textContent = online ? \"Online\" : \"Offline mode\";\r\n // If it becomes online, reload after tiny delay (feels smoother)\r\n if (online){\r\n showToast(\"✅ Back online — reloading…\");\r\n setTimeout(() => location.reload(), 650);\r\n }\r\n }\r\n\r\n retryBtn.addEventListener(\"click\", () => location.reload());\r\n\r\n copyBtn.addEventListener(\"click\", async () => {\r\n try{\r\n await navigator.clipboard.writeText(location.href);\r\n showToast(\"📋 URL copied!\");\r\n }catch{\r\n // fallback\r\n const ta = document.createElement(\"textarea\");\r\n ta.value = location.href;\r\n document.body.appendChild(ta);\r\n ta.select();\r\n document.execCommand(\"copy\");\r\n ta.remove();\r\n showToast(\"📋 URL copied!\");\r\n }\r\n });\r\n\r\n window.addEventListener(\"online\", updateStatus);\r\n window.addEventListener(\"offline\", updateStatus);\r\n\r\n // Keyboard shortcuts\r\n window.addEventListener(\"keydown\", (e) => {\r\n const k = (e.key || \"\").toLowerCase();\r\n if (k === \"r\") location.reload();\r\n if (k === \"h\") location.href = \"/\";\r\n });\r\n\r\n // initial\r\n updateStatus();\r\n </script>\r\n</body>\r\n</html>`;\r\n}","export function offlineSvgTemplate() {\r\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"630\" viewBox=\"0 0 1200 630\">\r\n <rect width=\"1200\" height=\"630\" fill=\"#0b0f19\"/>\r\n <text x=\"80\" y=\"220\" fill=\"#ffffff\" font-size=\"64\" font-family=\"system-ui, -apple-system, Segoe UI, Roboto\">You’re Offline</text>\r\n <text x=\"80\" y=\"300\" fill=\"#cbd5e1\" font-size=\"28\" font-family=\"system-ui, -apple-system, Segoe UI, Roboto\">Please check your connection and try again.</text>\r\n <circle cx=\"1040\" cy=\"220\" r=\"90\" fill=\"rgba(255,255,255,0.08)\"/>\r\n <path d=\"M980 220c40-40 120-40 160 0\" stroke=\"#fff\" stroke-width=\"10\" fill=\"none\" opacity=\"0.6\"/>\r\n <path d=\"M1010 250c25-25 75-25 100 0\" stroke=\"#fff\" stroke-width=\"10\" fill=\"none\" opacity=\"0.6\"/>\r\n <circle cx=\"1060\" cy=\"290\" r=\"10\" fill=\"#fff\" opacity=\"0.7\"/>\r\n</svg>`;\r\n}","import type { OfflineKitBuildOptions } from \"../types\";\r\n\r\nconst js = (v: unknown) => JSON.stringify(v);\r\n\r\nexport function buildServiceWorkerJS(options: Required<OfflineKitBuildOptions>) {\r\n const { cacheName, offlinePage, offlineImage } = options;\r\n\r\n return `/* offline-page-kit service worker (minimal) */\r\nconst CACHE_NAME = ${js(cacheName)};\r\nconst OFFLINE_PAGE = ${js(offlinePage)};\r\nconst OFFLINE_IMAGE = ${js(offlineImage)};\r\n\r\nself.addEventListener(\"install\", (event) => {\r\n event.waitUntil((async () => {\r\n const cache = await caches.open(CACHE_NAME);\r\n\r\n // ✅ Do not let one 404 kill the install\r\n await Promise.allSettled([\r\n cache.add(OFFLINE_PAGE),\r\n cache.add(OFFLINE_IMAGE),\r\n ]);\r\n\r\n await self.skipWaiting();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"activate\", (event) => {\r\n event.waitUntil((async () => {\r\n await self.clients.claim();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"fetch\", (event) => {\r\n const req = event.request;\r\n\r\n // ✅ Offline fallback only for page navigations\r\n if (req.mode === \"navigate\") {\r\n event.respondWith((async () => {\r\n try {\r\n return await fetch(req);\r\n } catch {\r\n const cache = await caches.open(CACHE_NAME);\r\n return (await cache.match(OFFLINE_PAGE)) || new Response(\"Offline\", { status: 503 });\r\n }\r\n })());\r\n }\r\n});\r\n`;\r\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AACA,IAAAA,oBAAiB;;;ACDjB,qBAAe;AACf,uBAAiB;AAEV,SAAS,UAAU,GAAW;AACjC,iBAAAC,QAAG,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AACvC;AAEO,SAAS,cAAc,UAAkB,SAAiB;AAC7D,YAAU,iBAAAC,QAAK,QAAQ,QAAQ,CAAC;AAChC,iBAAAD,QAAG,cAAc,UAAU,SAAS,MAAM;AAC9C;AAEO,SAAS,OAAO,UAAkB;AACrC,MAAI;AAAE,mBAAAA,QAAG,WAAW,QAAQ;AAAG,WAAO;AAAA,EAAM,QAAQ;AAAE,WAAO;AAAA,EAAO;AACxE;AAEO,SAAS,UAAU,MAAgB;AACtC,QAAM,IAAI,oBAAI,IAAoB;AAElC,QAAM,OAAO,CAAC,MAAc,EAAE,QAAQ,MAAM,EAAE,EAAE,YAAY;AAE5D,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AAClC,UAAM,IAAI,KAAK,CAAC;AAChB,QAAI,CAAC,EAAE,WAAW,IAAI,EAAG;AAEzB,UAAM,SAAS,EAAE,MAAM,CAAC;AACxB,UAAM,MAAM,KAAK,MAAM;AAEvB,UAAM,QAAQ,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,WAAW,IAAI,IAAI,KAAK,EAAE,CAAC,IAAI;AACzE,MAAE,IAAI,KAAK,KAAK;AAAA,EACpB;AAEA,SAAO;AAAA,IACH,IAAI,MAAc;AACd,aAAO,EAAE,IAAI,KAAK,IAAI,CAAC;AAAA,IAC3B;AAAA,EACJ;AACJ;AAEO,SAAS,UAAU,GAAuB;AAC7C,UAAQ,KAAK,IACR,MAAM,GAAG,EACT,IAAI,OAAK,EAAE,KAAK,CAAC,EACjB,OAAO,OAAO;AACvB;;;AC5CO,SAAS,oBAAoB,QAAQ,kBAAkB;AAC5D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,WAKE,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;A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","names":["import_node_path","fs","path","options","path"]}
|
package/dist/cli.js
CHANGED
|
@@ -23,14 +23,20 @@ function exists(filePath) {
|
|
|
23
23
|
}
|
|
24
24
|
function parseArgs(argv) {
|
|
25
25
|
const m = /* @__PURE__ */ new Map();
|
|
26
|
+
const norm = (k) => k.replace(/-/g, "").toLowerCase();
|
|
26
27
|
for (let i = 0; i < argv.length; i++) {
|
|
27
28
|
const a = argv[i];
|
|
28
29
|
if (!a.startsWith("--")) continue;
|
|
29
|
-
const
|
|
30
|
+
const rawKey = a.slice(2);
|
|
31
|
+
const key = norm(rawKey);
|
|
30
32
|
const value = argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[++i] : "true";
|
|
31
33
|
m.set(key, value);
|
|
32
34
|
}
|
|
33
|
-
return
|
|
35
|
+
return {
|
|
36
|
+
get(name) {
|
|
37
|
+
return m.get(norm(name));
|
|
38
|
+
}
|
|
39
|
+
};
|
|
34
40
|
}
|
|
35
41
|
function splitList(v) {
|
|
36
42
|
return (v || "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
@@ -44,26 +50,403 @@ function offlineHtmlTemplate(title = "You're Offline") {
|
|
|
44
50
|
<meta charset="UTF-8"/>
|
|
45
51
|
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
|
46
52
|
<title>${title}</title>
|
|
53
|
+
<meta name="theme-color" content="#0b0f19"/>
|
|
54
|
+
|
|
47
55
|
<style>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
:root{
|
|
57
|
+
--bg0:#060912;
|
|
58
|
+
--bg1:#0b1022;
|
|
59
|
+
--card: rgba(255,255,255,.06);
|
|
60
|
+
--card2: rgba(255,255,255,.04);
|
|
61
|
+
--stroke: rgba(255,255,255,.14);
|
|
62
|
+
--text:#ffffff;
|
|
63
|
+
--muted: rgba(255,255,255,.78);
|
|
64
|
+
--muted2: rgba(255,255,255,.62);
|
|
65
|
+
--good:#2ee59d;
|
|
66
|
+
--warn:#ffd166;
|
|
67
|
+
--shadow: 0 20px 80px rgba(0,0,0,.55);
|
|
68
|
+
--radius: 22px;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
*{box-sizing:border-box}
|
|
72
|
+
html,body{height:100%}
|
|
73
|
+
body{
|
|
74
|
+
margin:0;
|
|
75
|
+
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji";
|
|
76
|
+
color:var(--text);
|
|
77
|
+
background:
|
|
78
|
+
radial-gradient(900px 500px at 18% 18%, rgba(120,140,255,.20), transparent 60%),
|
|
79
|
+
radial-gradient(700px 500px at 82% 20%, rgba(46,229,157,.16), transparent 58%),
|
|
80
|
+
radial-gradient(600px 520px at 50% 95%, rgba(255,209,102,.14), transparent 55%),
|
|
81
|
+
linear-gradient(180deg, var(--bg0), var(--bg1));
|
|
82
|
+
overflow:hidden;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* Floating blobs (subtle animation) */
|
|
86
|
+
.blob{
|
|
87
|
+
position:absolute; inset:auto;
|
|
88
|
+
width: 520px; height: 520px;
|
|
89
|
+
border-radius: 50%;
|
|
90
|
+
filter: blur(42px);
|
|
91
|
+
opacity:.35;
|
|
92
|
+
animation: float 10s ease-in-out infinite;
|
|
93
|
+
pointer-events:none;
|
|
94
|
+
transform: translate3d(0,0,0);
|
|
95
|
+
}
|
|
96
|
+
.blob.b1{left:-180px; top:-200px; background: rgba(120,140,255,.55); animation-duration: 12s;}
|
|
97
|
+
.blob.b2{right:-210px; top:-150px; background: rgba(46,229,157,.52); animation-duration: 14s;}
|
|
98
|
+
.blob.b3{left:18%; bottom:-320px; background: rgba(255,209,102,.45); animation-duration: 16s;}
|
|
99
|
+
|
|
100
|
+
@keyframes float{
|
|
101
|
+
0%,100%{ transform: translate(0,0) scale(1);}
|
|
102
|
+
50%{ transform: translate(22px,-18px) scale(1.06);}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* Main layout */
|
|
106
|
+
.wrap{
|
|
107
|
+
min-height:100%;
|
|
108
|
+
display:grid;
|
|
109
|
+
place-items:center;
|
|
110
|
+
padding: 24px;
|
|
111
|
+
position:relative;
|
|
112
|
+
z-index:1;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.card{
|
|
116
|
+
width:min(720px, 100%);
|
|
117
|
+
border-radius: var(--radius);
|
|
118
|
+
background: linear-gradient(180deg, var(--card), var(--card2));
|
|
119
|
+
border:1px solid var(--stroke);
|
|
120
|
+
box-shadow: var(--shadow);
|
|
121
|
+
padding: 26px;
|
|
122
|
+
backdrop-filter: blur(10px);
|
|
123
|
+
-webkit-backdrop-filter: blur(10px);
|
|
124
|
+
|
|
125
|
+
animation: pop .55s cubic-bezier(.2,.9,.2,1) both;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
@keyframes pop{
|
|
129
|
+
from{ opacity:0; transform: translateY(14px) scale(.98); }
|
|
130
|
+
to{ opacity:1; transform: translateY(0) scale(1); }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.top{
|
|
134
|
+
display:flex;
|
|
135
|
+
align-items:flex-start;
|
|
136
|
+
justify-content:space-between;
|
|
137
|
+
gap:16px;
|
|
138
|
+
margin-bottom: 14px;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.badge{
|
|
142
|
+
display:inline-flex;
|
|
143
|
+
align-items:center;
|
|
144
|
+
gap:10px;
|
|
145
|
+
padding: 10px 12px;
|
|
146
|
+
border-radius: 999px;
|
|
147
|
+
border:1px solid rgba(255,255,255,.14);
|
|
148
|
+
background: rgba(0,0,0,.18);
|
|
149
|
+
color: var(--muted);
|
|
150
|
+
font-size: 13px;
|
|
151
|
+
user-select:none;
|
|
152
|
+
white-space:nowrap;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* Ping dot */
|
|
156
|
+
.dot{
|
|
157
|
+
width:10px;height:10px;border-radius:50%;
|
|
158
|
+
background: var(--warn);
|
|
159
|
+
box-shadow: 0 0 0 0 rgba(255,209,102,.65);
|
|
160
|
+
animation: ping 1.6s infinite;
|
|
161
|
+
}
|
|
162
|
+
@keyframes ping{
|
|
163
|
+
0%{ box-shadow: 0 0 0 0 rgba(255,209,102,.55); }
|
|
164
|
+
70%{ box-shadow: 0 0 0 10px rgba(255,209,102,0); }
|
|
165
|
+
100%{ box-shadow: 0 0 0 0 rgba(255,209,102,0); }
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
h1{
|
|
169
|
+
margin: 0 0 8px;
|
|
170
|
+
font-size: clamp(26px, 3.3vw, 34px);
|
|
171
|
+
letter-spacing: -0.02em;
|
|
172
|
+
line-height: 1.15;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
p{
|
|
176
|
+
margin: 0;
|
|
177
|
+
color: var(--muted2);
|
|
178
|
+
line-height: 1.55;
|
|
179
|
+
font-size: 15.5px;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.hero{
|
|
183
|
+
display:flex;
|
|
184
|
+
align-items:center;
|
|
185
|
+
gap: 14px;
|
|
186
|
+
margin-top: 10px;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/* Animated wifi icon */
|
|
190
|
+
.wifi{
|
|
191
|
+
width: 54px; height: 54px;
|
|
192
|
+
border-radius: 16px;
|
|
193
|
+
display:grid; place-items:center;
|
|
194
|
+
background: rgba(255,255,255,.06);
|
|
195
|
+
border:1px solid rgba(255,255,255,.12);
|
|
196
|
+
position:relative;
|
|
197
|
+
overflow:hidden;
|
|
198
|
+
}
|
|
199
|
+
.wifi::after{
|
|
200
|
+
content:"";
|
|
201
|
+
position:absolute; inset:-40%;
|
|
202
|
+
background: radial-gradient(circle at 30% 30%, rgba(255,255,255,.18), transparent 55%);
|
|
203
|
+
animation: sheen 2.8s ease-in-out infinite;
|
|
204
|
+
}
|
|
205
|
+
@keyframes sheen{
|
|
206
|
+
0%,100%{ transform: translate(-8px,-6px) rotate(0deg); opacity:.75;}
|
|
207
|
+
50%{ transform: translate(10px,8px) rotate(8deg); opacity:.9;}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.wifi svg{ position:relative; z-index:1; opacity:.95; }
|
|
211
|
+
.wifi path{ animation: wave 1.6s ease-in-out infinite; transform-origin:center; }
|
|
212
|
+
.wifi path:nth-child(1){ opacity:.35; animation-delay:.05s;}
|
|
213
|
+
.wifi path:nth-child(2){ opacity:.55; animation-delay:.12s;}
|
|
214
|
+
.wifi path:nth-child(3){ opacity:.75; animation-delay:.2s;}
|
|
215
|
+
.wifi circle{ opacity:.9; animation: blink 1.2s ease-in-out infinite; }
|
|
216
|
+
|
|
217
|
+
@keyframes wave{
|
|
218
|
+
0%,100%{ transform: scale(1); }
|
|
219
|
+
50%{ transform: scale(1.06); }
|
|
220
|
+
}
|
|
221
|
+
@keyframes blink{
|
|
222
|
+
0%,100%{ opacity:.9; }
|
|
223
|
+
50%{ opacity:.45; }
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.actions{
|
|
227
|
+
display:flex;
|
|
228
|
+
flex-wrap:wrap;
|
|
229
|
+
gap: 10px;
|
|
230
|
+
margin-top: 18px;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
.btn{
|
|
234
|
+
appearance:none;
|
|
235
|
+
border:0;
|
|
236
|
+
border-radius: 14px;
|
|
237
|
+
padding: 11px 14px;
|
|
238
|
+
cursor:pointer;
|
|
239
|
+
text-decoration:none;
|
|
240
|
+
display:inline-flex;
|
|
241
|
+
align-items:center;
|
|
242
|
+
gap:10px;
|
|
243
|
+
font-weight: 650;
|
|
244
|
+
font-size: 14.5px;
|
|
245
|
+
letter-spacing: .01em;
|
|
246
|
+
transition: transform .12s ease, filter .12s ease, background .2s ease, border-color .2s ease;
|
|
247
|
+
user-select:none;
|
|
248
|
+
}
|
|
249
|
+
.btn:active{ transform: translateY(1px) scale(.99); }
|
|
250
|
+
|
|
251
|
+
.primary{
|
|
252
|
+
background: #ffffff;
|
|
253
|
+
color: #0b0f19;
|
|
254
|
+
box-shadow: 0 8px 24px rgba(255,255,255,.12);
|
|
255
|
+
}
|
|
256
|
+
.primary:hover{ filter: brightness(0.98); }
|
|
257
|
+
|
|
258
|
+
.ghost{
|
|
259
|
+
background: rgba(255,255,255,.08);
|
|
260
|
+
color: var(--text);
|
|
261
|
+
border: 1px solid rgba(255,255,255,.14);
|
|
262
|
+
}
|
|
263
|
+
.ghost:hover{
|
|
264
|
+
background: rgba(255,255,255,.10);
|
|
265
|
+
border-color: rgba(255,255,255,.18);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.hint{
|
|
269
|
+
margin-top: 14px;
|
|
270
|
+
display:flex;
|
|
271
|
+
gap:10px;
|
|
272
|
+
flex-wrap:wrap;
|
|
273
|
+
align-items:center;
|
|
274
|
+
color: var(--muted2);
|
|
275
|
+
font-size: 13px;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
.kbd{
|
|
279
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
|
280
|
+
font-size: 12px;
|
|
281
|
+
padding: 4px 8px;
|
|
282
|
+
border-radius: 9px;
|
|
283
|
+
border:1px solid rgba(255,255,255,.14);
|
|
284
|
+
background: rgba(0,0,0,.20);
|
|
285
|
+
color: rgba(255,255,255,.82);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/* Toast */
|
|
289
|
+
.toast{
|
|
290
|
+
position: fixed;
|
|
291
|
+
left: 50%;
|
|
292
|
+
bottom: 18px;
|
|
293
|
+
transform: translateX(-50%);
|
|
294
|
+
padding: 10px 12px;
|
|
295
|
+
border-radius: 999px;
|
|
296
|
+
background: rgba(0,0,0,.40);
|
|
297
|
+
border:1px solid rgba(255,255,255,.14);
|
|
298
|
+
color: rgba(255,255,255,.86);
|
|
299
|
+
font-size: 13px;
|
|
300
|
+
display:flex;
|
|
301
|
+
gap:10px;
|
|
302
|
+
align-items:center;
|
|
303
|
+
backdrop-filter: blur(10px);
|
|
304
|
+
-webkit-backdrop-filter: blur(10px);
|
|
305
|
+
box-shadow: 0 10px 40px rgba(0,0,0,.35);
|
|
306
|
+
opacity:0;
|
|
307
|
+
pointer-events:none;
|
|
308
|
+
transition: opacity .22s ease, transform .22s ease;
|
|
309
|
+
}
|
|
310
|
+
.toast.show{
|
|
311
|
+
opacity:1;
|
|
312
|
+
transform: translateX(-50%) translateY(-2px);
|
|
313
|
+
}
|
|
314
|
+
.toast .okdot{
|
|
315
|
+
width:10px;height:10px;border-radius:50%;
|
|
316
|
+
background: var(--good);
|
|
317
|
+
box-shadow: 0 0 0 6px rgba(46,229,157,.10);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/* Reduce motion */
|
|
321
|
+
@media (prefers-reduced-motion: reduce){
|
|
322
|
+
.blob, .dot, .wifi path, .wifi circle, .card{ animation:none !important; }
|
|
323
|
+
.toast{ transition:none; }
|
|
324
|
+
}
|
|
56
325
|
</style>
|
|
57
326
|
</head>
|
|
327
|
+
|
|
58
328
|
<body>
|
|
59
|
-
<div class="
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
329
|
+
<div class="blob b1"></div>
|
|
330
|
+
<div class="blob b2"></div>
|
|
331
|
+
<div class="blob b3"></div>
|
|
332
|
+
|
|
333
|
+
<main class="wrap" role="main">
|
|
334
|
+
<section class="card" aria-labelledby="offline-title">
|
|
335
|
+
<div class="top">
|
|
336
|
+
<div>
|
|
337
|
+
<h1 id="offline-title">\u{1F635}\u200D\u{1F4AB} You\u2019re Offline</h1>
|
|
338
|
+
<p>
|
|
339
|
+
No internet connection detected. Don\u2019t worry \u2014 you can retry now,
|
|
340
|
+
or go home if it\u2019s already cached. \u2728
|
|
341
|
+
</p>
|
|
342
|
+
</div>
|
|
343
|
+
|
|
344
|
+
<div class="badge" aria-live="polite">
|
|
345
|
+
<span class="dot" aria-hidden="true"></span>
|
|
346
|
+
<span id="netText">Offline mode</span>
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
|
|
350
|
+
<div class="hero">
|
|
351
|
+
<div class="wifi" aria-hidden="true">
|
|
352
|
+
<svg width="30" height="30" viewBox="0 0 24 24" fill="none">
|
|
353
|
+
<path d="M2.5 9.2C8.6 3.2 15.4 3.2 21.5 9.2" stroke="white" stroke-width="2" stroke-linecap="round"/>
|
|
354
|
+
<path d="M5.8 12.4C10.1 8.3 13.9 8.3 18.2 12.4" stroke="white" stroke-width="2" stroke-linecap="round"/>
|
|
355
|
+
<path d="M9.2 15.7C11.2 13.9 12.8 13.9 14.8 15.7" stroke="white" stroke-width="2" stroke-linecap="round"/>
|
|
356
|
+
<circle cx="12" cy="19" r="1.4" fill="white"/>
|
|
357
|
+
</svg>
|
|
358
|
+
</div>
|
|
359
|
+
|
|
360
|
+
<div>
|
|
361
|
+
<p style="margin:0 0 6px;color:rgba(255,255,255,.86);font-weight:650">
|
|
362
|
+
Tip: Your cached pages may still open \u26A1
|
|
363
|
+
</p>
|
|
364
|
+
<p style="margin:0;color:rgba(255,255,255,.62)">
|
|
365
|
+
We\u2019ll auto-refresh when you\u2019re back online \u2705
|
|
366
|
+
</p>
|
|
367
|
+
</div>
|
|
368
|
+
</div>
|
|
369
|
+
|
|
370
|
+
<div class="actions">
|
|
371
|
+
<button class="btn primary" id="retryBtn" type="button">
|
|
372
|
+
\u{1F504} Retry
|
|
373
|
+
</button>
|
|
374
|
+
<a class="btn ghost" href="/" rel="noopener">
|
|
375
|
+
\u{1F3E0} Go Home
|
|
376
|
+
</a>
|
|
377
|
+
<button class="btn ghost" id="copyBtn" type="button" title="Copy current URL">
|
|
378
|
+
\u{1F517} Copy URL
|
|
379
|
+
</button>
|
|
380
|
+
</div>
|
|
381
|
+
|
|
382
|
+
<div class="hint">
|
|
383
|
+
<span>Quick keys:</span>
|
|
384
|
+
<span class="kbd">R</span> Retry
|
|
385
|
+
<span class="kbd">H</span> Home
|
|
386
|
+
</div>
|
|
387
|
+
</section>
|
|
388
|
+
</main>
|
|
389
|
+
|
|
390
|
+
<div class="toast" id="toast" role="status" aria-live="polite">
|
|
391
|
+
<span class="okdot" aria-hidden="true"></span>
|
|
392
|
+
<span id="toastText">Back online \u2014 reloading\u2026</span>
|
|
66
393
|
</div>
|
|
394
|
+
|
|
395
|
+
<script>
|
|
396
|
+
const retryBtn = document.getElementById("retryBtn");
|
|
397
|
+
const copyBtn = document.getElementById("copyBtn");
|
|
398
|
+
const netText = document.getElementById("netText");
|
|
399
|
+
const toast = document.getElementById("toast");
|
|
400
|
+
const toastText= document.getElementById("toastText");
|
|
401
|
+
|
|
402
|
+
function showToast(msg){
|
|
403
|
+
toastText.textContent = msg;
|
|
404
|
+
toast.classList.add("show");
|
|
405
|
+
clearTimeout(showToast._t);
|
|
406
|
+
showToast._t = setTimeout(() => toast.classList.remove("show"), 2200);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function updateStatus(){
|
|
410
|
+
const online = navigator.onLine;
|
|
411
|
+
netText.textContent = online ? "Online" : "Offline mode";
|
|
412
|
+
// If it becomes online, reload after tiny delay (feels smoother)
|
|
413
|
+
if (online){
|
|
414
|
+
showToast("\u2705 Back online \u2014 reloading\u2026");
|
|
415
|
+
setTimeout(() => location.reload(), 650);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
retryBtn.addEventListener("click", () => location.reload());
|
|
420
|
+
|
|
421
|
+
copyBtn.addEventListener("click", async () => {
|
|
422
|
+
try{
|
|
423
|
+
await navigator.clipboard.writeText(location.href);
|
|
424
|
+
showToast("\u{1F4CB} URL copied!");
|
|
425
|
+
}catch{
|
|
426
|
+
// fallback
|
|
427
|
+
const ta = document.createElement("textarea");
|
|
428
|
+
ta.value = location.href;
|
|
429
|
+
document.body.appendChild(ta);
|
|
430
|
+
ta.select();
|
|
431
|
+
document.execCommand("copy");
|
|
432
|
+
ta.remove();
|
|
433
|
+
showToast("\u{1F4CB} URL copied!");
|
|
434
|
+
}
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
window.addEventListener("online", updateStatus);
|
|
438
|
+
window.addEventListener("offline", updateStatus);
|
|
439
|
+
|
|
440
|
+
// Keyboard shortcuts
|
|
441
|
+
window.addEventListener("keydown", (e) => {
|
|
442
|
+
const k = (e.key || "").toLowerCase();
|
|
443
|
+
if (k === "r") location.reload();
|
|
444
|
+
if (k === "h") location.href = "/";
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
// initial
|
|
448
|
+
updateStatus();
|
|
449
|
+
</script>
|
|
67
450
|
</body>
|
|
68
451
|
</html>`;
|
|
69
452
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/core/utils.ts","../src/core/offlineHtml.ts","../src/core/offlineSvg.ts","../src/core/swTemplate.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport path from \"node:path\";\r\nimport { parseArgs, splitList, writeFileSafe, exists } from \"./core/utils\";\r\nimport { offlineHtmlTemplate } from \"./core/offlineHtml\";\r\nimport { offlineSvgTemplate } from \"./core/offlineSvg\";\r\nimport { buildServiceWorkerJS } from \"./core/swTemplate\";\r\nimport type { OfflineKitBuildOptions } from \"./types\";\r\n\r\nfunction normalizePublicPath(p: string) {\r\n if (!p.startsWith(\"/\")) return \"/\" + p;\r\n return p;\r\n}\r\n\r\nfunction withDefaults(o: OfflineKitBuildOptions): Required<OfflineKitBuildOptions> {\r\n return {\r\n outDir: o.outDir ?? \"public\",\r\n swFileName: o.swFileName ?? \"sw.js\",\r\n offlinePage: normalizePublicPath(o.offlinePage ?? \"/offline.html\"),\r\n offlineImage: normalizePublicPath(o.offlineImage ?? \"/offline.svg\"),\r\n cacheName: o.cacheName ?? \"offline-page-kit\",\r\n precache: o.precache ?? [],\r\n htmlStrategy: o.htmlStrategy ?? \"networkFirst\",\r\n assetStrategy: o.assetStrategy ?? \"staleWhileRevalidate\",\r\n imageStrategy: o.imageStrategy ?? \"cacheFirst\",\r\n assetExtensions: o.assetExtensions ?? [],\r\n apiPrefixes: o.apiPrefixes ?? [],\r\n };\r\n}\r\n\r\nconst args = parseArgs(process.argv.slice(2));\r\nconst cmd = process.argv.slice(2).find(a => !a.startsWith(\"--\")) || \"init\";\r\n\r\nconst outDir = path.resolve(process.cwd(), args.get(\"outDir\") || \"public\");\r\n\r\nconst options = withDefaults({\r\n outDir,\r\n swFileName: args.get(\"swFileName\") || \"sw.js\",\r\n offlinePage: args.get(\"offlinePage\") || \"/offline.html\",\r\n offlineImage: args.get(\"offlineImage\") || \"/offline.svg\",\r\n cacheName: args.get(\"cacheName\") || \"offline-page-kit\",\r\n precache: splitList(args.get(\"precache\")),\r\n htmlStrategy: (args.get(\"htmlStrategy\") as any) || \"networkFirst\",\r\n assetStrategy: (args.get(\"assetStrategy\") as any) || \"staleWhileRevalidate\",\r\n imageStrategy: (args.get(\"imageStrategy\") as any) || \"cacheFirst\",\r\n assetExtensions: splitList(args.get(\"assetExtensions\")),\r\n apiPrefixes: splitList(args.get(\"apiPrefixes\")),\r\n} as OfflineKitBuildOptions);\r\n\r\nconst swOut = path.join(outDir, options.swFileName);\r\nconst offlineHtmlOut = path.join(outDir, options.offlinePage.replace(/^\\//, \"\"));\r\nconst offlineSvgOut = path.join(outDir, options.offlineImage.replace(/^\\//, \"\"));\r\n\r\nif (cmd === \"init\" || cmd === \"build\") {\r\n // generate offline page if missing OR force (when build)\r\n if (cmd === \"build\" || !exists(offlineHtmlOut)) {\r\n writeFileSafe(offlineHtmlOut, offlineHtmlTemplate());\r\n }\r\n if (cmd === \"build\" || !exists(offlineSvgOut)) {\r\n writeFileSafe(offlineSvgOut, offlineSvgTemplate());\r\n }\r\n\r\n const sw = buildServiceWorkerJS(options);\r\n writeFileSafe(swOut, sw);\r\n\r\n console.log(`[offline-page-kit] Generated:\r\n- ${swOut}\r\n- ${offlineHtmlOut}\r\n- ${offlineSvgOut}\r\n`);\r\n} else {\r\n console.log(`[offline-page-kit] Unknown command: ${cmd}\r\nUse:\r\n offline-page-kit init --outDir public\r\n offline-page-kit build --outDir public\r\n`);\r\n}","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\n\r\nexport function ensureDir(p: string) {\r\n fs.mkdirSync(p, { recursive: true });\r\n}\r\n\r\nexport function writeFileSafe(filePath: string, content: string) {\r\n ensureDir(path.dirname(filePath));\r\n fs.writeFileSync(filePath, content, \"utf8\");\r\n}\r\n\r\nexport function exists(filePath: string) {\r\n try { fs.accessSync(filePath); return true; } catch { return false; }\r\n}\r\n\r\nexport function parseArgs(argv: string[]) {\r\n const m = new Map<string, string>();\r\n for (let i = 0; i < argv.length; i++) {\r\n const a = argv[i];\r\n if (!a.startsWith(\"--\")) continue;\r\n const key = a.slice(2);\r\n const value = argv[i + 1] && !argv[i + 1].startsWith(\"--\") ? argv[++i] : \"true\";\r\n m.set(key, value);\r\n }\r\n return m;\r\n}\r\n\r\nexport function splitList(v: string | undefined) {\r\n return (v || \"\")\r\n .split(\",\")\r\n .map(s => s.trim())\r\n .filter(Boolean);\r\n}","export function offlineHtmlTemplate(title = \"You're Offline\") {\r\n return `<!doctype html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"UTF-8\"/>\r\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>\r\n <title>${title}</title>\r\n <style>\r\n body{margin:0;height:100vh;display:grid;place-items:center;font-family:system-ui,-apple-system,Segoe UI,Roboto;background:#0b0f19;color:#fff}\r\n .box{max-width:560px;padding:28px 26px;border-radius:18px;border:1px solid rgba(255,255,255,.12);background:rgba(255,255,255,.04)}\r\n h1{margin:0 0 10px;font-size:28px}\r\n p{margin:0 0 18px;opacity:.85;line-height:1.5}\r\n .row{display:flex;gap:10px;flex-wrap:wrap}\r\n button,a{appearance:none;border:0;border-radius:12px;padding:10px 14px;cursor:pointer;text-decoration:none}\r\n button{background:#fff;color:#111}\r\n a{background:rgba(255,255,255,.1);color:#fff}\r\n </style>\r\n</head>\r\n<body>\r\n <div class=\"box\">\r\n <h1>⚡ You’re Offline</h1>\r\n <p>No internet connection detected. You can retry, or go back to the homepage (if cached).</p>\r\n <div class=\"row\">\r\n <button onclick=\"location.reload()\">Retry</button>\r\n <a href=\"/\">Go Home</a>\r\n </div>\r\n </div>\r\n</body>\r\n</html>`;\r\n}","export function offlineSvgTemplate() {\r\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"630\" viewBox=\"0 0 1200 630\">\r\n <rect width=\"1200\" height=\"630\" fill=\"#0b0f19\"/>\r\n <text x=\"80\" y=\"220\" fill=\"#ffffff\" font-size=\"64\" font-family=\"system-ui, -apple-system, Segoe UI, Roboto\">You’re Offline</text>\r\n <text x=\"80\" y=\"300\" fill=\"#cbd5e1\" font-size=\"28\" font-family=\"system-ui, -apple-system, Segoe UI, Roboto\">Please check your connection and try again.</text>\r\n <circle cx=\"1040\" cy=\"220\" r=\"90\" fill=\"rgba(255,255,255,0.08)\"/>\r\n <path d=\"M980 220c40-40 120-40 160 0\" stroke=\"#fff\" stroke-width=\"10\" fill=\"none\" opacity=\"0.6\"/>\r\n <path d=\"M1010 250c25-25 75-25 100 0\" stroke=\"#fff\" stroke-width=\"10\" fill=\"none\" opacity=\"0.6\"/>\r\n <circle cx=\"1060\" cy=\"290\" r=\"10\" fill=\"#fff\" opacity=\"0.7\"/>\r\n</svg>`;\r\n}","import type { OfflineKitBuildOptions } from \"../types\";\r\n\r\nconst js = (v: unknown) => JSON.stringify(v);\r\n\r\nexport function buildServiceWorkerJS(options: Required<OfflineKitBuildOptions>) {\r\n const { cacheName, offlinePage, offlineImage } = options;\r\n\r\n return `/* offline-page-kit service worker (minimal) */\r\nconst CACHE_NAME = ${js(cacheName)};\r\nconst OFFLINE_PAGE = ${js(offlinePage)};\r\nconst OFFLINE_IMAGE = ${js(offlineImage)};\r\n\r\nself.addEventListener(\"install\", (event) => {\r\n event.waitUntil((async () => {\r\n const cache = await caches.open(CACHE_NAME);\r\n\r\n // ✅ Do not let one 404 kill the install\r\n await Promise.allSettled([\r\n cache.add(OFFLINE_PAGE),\r\n cache.add(OFFLINE_IMAGE),\r\n ]);\r\n\r\n await self.skipWaiting();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"activate\", (event) => {\r\n event.waitUntil((async () => {\r\n await self.clients.claim();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"fetch\", (event) => {\r\n const req = event.request;\r\n\r\n // ✅ Offline fallback only for page navigations\r\n if (req.mode === \"navigate\") {\r\n event.respondWith((async () => {\r\n try {\r\n return await fetch(req);\r\n } catch {\r\n const cache = await caches.open(CACHE_NAME);\r\n return (await cache.match(OFFLINE_PAGE)) || new Response(\"Offline\", { status: 503 });\r\n }\r\n })());\r\n }\r\n});\r\n`;\r\n}"],"mappings":"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","names":["path","options","path"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/core/utils.ts","../src/core/offlineHtml.ts","../src/core/offlineSvg.ts","../src/core/swTemplate.ts"],"sourcesContent":["#!/usr/bin/env node\r\nimport path from \"node:path\";\r\nimport { parseArgs, splitList, writeFileSafe, exists } from \"./core/utils\";\r\nimport { offlineHtmlTemplate } from \"./core/offlineHtml\";\r\nimport { offlineSvgTemplate } from \"./core/offlineSvg\";\r\nimport { buildServiceWorkerJS } from \"./core/swTemplate\";\r\nimport type { OfflineKitBuildOptions } from \"./types\";\r\n\r\nfunction normalizePublicPath(p: string) {\r\n if (!p.startsWith(\"/\")) return \"/\" + p;\r\n return p;\r\n}\r\n\r\nfunction withDefaults(o: OfflineKitBuildOptions): Required<OfflineKitBuildOptions> {\r\n return {\r\n outDir: o.outDir ?? \"public\",\r\n swFileName: o.swFileName ?? \"sw.js\",\r\n offlinePage: normalizePublicPath(o.offlinePage ?? \"/offline.html\"),\r\n offlineImage: normalizePublicPath(o.offlineImage ?? \"/offline.svg\"),\r\n cacheName: o.cacheName ?? \"offline-page-kit\",\r\n precache: o.precache ?? [],\r\n htmlStrategy: o.htmlStrategy ?? \"networkFirst\",\r\n assetStrategy: o.assetStrategy ?? \"staleWhileRevalidate\",\r\n imageStrategy: o.imageStrategy ?? \"cacheFirst\",\r\n assetExtensions: o.assetExtensions ?? [],\r\n apiPrefixes: o.apiPrefixes ?? [],\r\n };\r\n}\r\n\r\nconst args = parseArgs(process.argv.slice(2));\r\nconst cmd = process.argv.slice(2).find(a => !a.startsWith(\"--\")) || \"init\";\r\n\r\nconst outDir = path.resolve(process.cwd(), args.get(\"outDir\") || \"public\");\r\n\r\nconst options = withDefaults({\r\n outDir,\r\n swFileName: args.get(\"swFileName\") || \"sw.js\",\r\n offlinePage: args.get(\"offlinePage\") || \"/offline.html\",\r\n offlineImage: args.get(\"offlineImage\") || \"/offline.svg\",\r\n cacheName: args.get(\"cacheName\") || \"offline-page-kit\",\r\n precache: splitList(args.get(\"precache\")),\r\n htmlStrategy: (args.get(\"htmlStrategy\") as any) || \"networkFirst\",\r\n assetStrategy: (args.get(\"assetStrategy\") as any) || \"staleWhileRevalidate\",\r\n imageStrategy: (args.get(\"imageStrategy\") as any) || \"cacheFirst\",\r\n assetExtensions: splitList(args.get(\"assetExtensions\")),\r\n apiPrefixes: splitList(args.get(\"apiPrefixes\")),\r\n} as OfflineKitBuildOptions);\r\n\r\nconst swOut = path.join(outDir, options.swFileName);\r\nconst offlineHtmlOut = path.join(outDir, options.offlinePage.replace(/^\\//, \"\"));\r\nconst offlineSvgOut = path.join(outDir, options.offlineImage.replace(/^\\//, \"\"));\r\n\r\nif (cmd === \"init\" || cmd === \"build\") {\r\n // generate offline page if missing OR force (when build)\r\n if (cmd === \"build\" || !exists(offlineHtmlOut)) {\r\n writeFileSafe(offlineHtmlOut, offlineHtmlTemplate());\r\n }\r\n if (cmd === \"build\" || !exists(offlineSvgOut)) {\r\n writeFileSafe(offlineSvgOut, offlineSvgTemplate());\r\n }\r\n\r\n const sw = buildServiceWorkerJS(options);\r\n writeFileSafe(swOut, sw);\r\n\r\n console.log(`[offline-page-kit] Generated:\r\n- ${swOut}\r\n- ${offlineHtmlOut}\r\n- ${offlineSvgOut}\r\n`);\r\n} else {\r\n console.log(`[offline-page-kit] Unknown command: ${cmd}\r\nUse:\r\n offline-page-kit init --outDir public\r\n offline-page-kit build --outDir public\r\n`);\r\n}","import fs from \"node:fs\";\r\nimport path from \"node:path\";\r\n\r\nexport function ensureDir(p: string) {\r\n fs.mkdirSync(p, { recursive: true });\r\n}\r\n\r\nexport function writeFileSafe(filePath: string, content: string) {\r\n ensureDir(path.dirname(filePath));\r\n fs.writeFileSync(filePath, content, \"utf8\");\r\n}\r\n\r\nexport function exists(filePath: string) {\r\n try { fs.accessSync(filePath); return true; } catch { return false; }\r\n}\r\n\r\nexport function parseArgs(argv: string[]) {\r\n const m = new Map<string, string>();\r\n\r\n const norm = (k: string) => k.replace(/-/g, \"\").toLowerCase();\r\n\r\n for (let i = 0; i < argv.length; i++) {\r\n const a = argv[i];\r\n if (!a.startsWith(\"--\")) continue;\r\n\r\n const rawKey = a.slice(2);\r\n const key = norm(rawKey);\r\n\r\n const value = argv[i + 1] && !argv[i + 1].startsWith(\"--\") ? argv[++i] : \"true\";\r\n m.set(key, value);\r\n }\r\n\r\n return {\r\n get(name: string) {\r\n return m.get(norm(name));\r\n }\r\n } as unknown as Map<string, string>;\r\n}\r\n\r\nexport function splitList(v: string | undefined) {\r\n return (v || \"\")\r\n .split(\",\")\r\n .map(s => s.trim())\r\n .filter(Boolean);\r\n}","export function offlineHtmlTemplate(title = \"You're Offline\") {\r\n return `<!doctype html>\r\n<html lang=\"en\">\r\n<head>\r\n <meta charset=\"UTF-8\"/>\r\n <meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>\r\n <title>${title}</title>\r\n <meta name=\"theme-color\" content=\"#0b0f19\"/>\r\n\r\n <style>\r\n :root{\r\n --bg0:#060912;\r\n --bg1:#0b1022;\r\n --card: rgba(255,255,255,.06);\r\n --card2: rgba(255,255,255,.04);\r\n --stroke: rgba(255,255,255,.14);\r\n --text:#ffffff;\r\n --muted: rgba(255,255,255,.78);\r\n --muted2: rgba(255,255,255,.62);\r\n --good:#2ee59d;\r\n --warn:#ffd166;\r\n --shadow: 0 20px 80px rgba(0,0,0,.55);\r\n --radius: 22px;\r\n }\r\n\r\n *{box-sizing:border-box}\r\n html,body{height:100%}\r\n body{\r\n margin:0;\r\n font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, \"Apple Color Emoji\",\"Segoe UI Emoji\";\r\n color:var(--text);\r\n background:\r\n radial-gradient(900px 500px at 18% 18%, rgba(120,140,255,.20), transparent 60%),\r\n radial-gradient(700px 500px at 82% 20%, rgba(46,229,157,.16), transparent 58%),\r\n radial-gradient(600px 520px at 50% 95%, rgba(255,209,102,.14), transparent 55%),\r\n linear-gradient(180deg, var(--bg0), var(--bg1));\r\n overflow:hidden;\r\n }\r\n\r\n /* Floating blobs (subtle animation) */\r\n .blob{\r\n position:absolute; inset:auto;\r\n width: 520px; height: 520px;\r\n border-radius: 50%;\r\n filter: blur(42px);\r\n opacity:.35;\r\n animation: float 10s ease-in-out infinite;\r\n pointer-events:none;\r\n transform: translate3d(0,0,0);\r\n }\r\n .blob.b1{left:-180px; top:-200px; background: rgba(120,140,255,.55); animation-duration: 12s;}\r\n .blob.b2{right:-210px; top:-150px; background: rgba(46,229,157,.52); animation-duration: 14s;}\r\n .blob.b3{left:18%; bottom:-320px; background: rgba(255,209,102,.45); animation-duration: 16s;}\r\n\r\n @keyframes float{\r\n 0%,100%{ transform: translate(0,0) scale(1);}\r\n 50%{ transform: translate(22px,-18px) scale(1.06);}\r\n }\r\n\r\n /* Main layout */\r\n .wrap{\r\n min-height:100%;\r\n display:grid;\r\n place-items:center;\r\n padding: 24px;\r\n position:relative;\r\n z-index:1;\r\n }\r\n\r\n .card{\r\n width:min(720px, 100%);\r\n border-radius: var(--radius);\r\n background: linear-gradient(180deg, var(--card), var(--card2));\r\n border:1px solid var(--stroke);\r\n box-shadow: var(--shadow);\r\n padding: 26px;\r\n backdrop-filter: blur(10px);\r\n -webkit-backdrop-filter: blur(10px);\r\n\r\n animation: pop .55s cubic-bezier(.2,.9,.2,1) both;\r\n }\r\n\r\n @keyframes pop{\r\n from{ opacity:0; transform: translateY(14px) scale(.98); }\r\n to{ opacity:1; transform: translateY(0) scale(1); }\r\n }\r\n\r\n .top{\r\n display:flex;\r\n align-items:flex-start;\r\n justify-content:space-between;\r\n gap:16px;\r\n margin-bottom: 14px;\r\n }\r\n\r\n .badge{\r\n display:inline-flex;\r\n align-items:center;\r\n gap:10px;\r\n padding: 10px 12px;\r\n border-radius: 999px;\r\n border:1px solid rgba(255,255,255,.14);\r\n background: rgba(0,0,0,.18);\r\n color: var(--muted);\r\n font-size: 13px;\r\n user-select:none;\r\n white-space:nowrap;\r\n }\r\n\r\n /* Ping dot */\r\n .dot{\r\n width:10px;height:10px;border-radius:50%;\r\n background: var(--warn);\r\n box-shadow: 0 0 0 0 rgba(255,209,102,.65);\r\n animation: ping 1.6s infinite;\r\n }\r\n @keyframes ping{\r\n 0%{ box-shadow: 0 0 0 0 rgba(255,209,102,.55); }\r\n 70%{ box-shadow: 0 0 0 10px rgba(255,209,102,0); }\r\n 100%{ box-shadow: 0 0 0 0 rgba(255,209,102,0); }\r\n }\r\n\r\n h1{\r\n margin: 0 0 8px;\r\n font-size: clamp(26px, 3.3vw, 34px);\r\n letter-spacing: -0.02em;\r\n line-height: 1.15;\r\n }\r\n\r\n p{\r\n margin: 0;\r\n color: var(--muted2);\r\n line-height: 1.55;\r\n font-size: 15.5px;\r\n }\r\n\r\n .hero{\r\n display:flex;\r\n align-items:center;\r\n gap: 14px;\r\n margin-top: 10px;\r\n }\r\n\r\n /* Animated wifi icon */\r\n .wifi{\r\n width: 54px; height: 54px;\r\n border-radius: 16px;\r\n display:grid; place-items:center;\r\n background: rgba(255,255,255,.06);\r\n border:1px solid rgba(255,255,255,.12);\r\n position:relative;\r\n overflow:hidden;\r\n }\r\n .wifi::after{\r\n content:\"\";\r\n position:absolute; inset:-40%;\r\n background: radial-gradient(circle at 30% 30%, rgba(255,255,255,.18), transparent 55%);\r\n animation: sheen 2.8s ease-in-out infinite;\r\n }\r\n @keyframes sheen{\r\n 0%,100%{ transform: translate(-8px,-6px) rotate(0deg); opacity:.75;}\r\n 50%{ transform: translate(10px,8px) rotate(8deg); opacity:.9;}\r\n }\r\n\r\n .wifi svg{ position:relative; z-index:1; opacity:.95; }\r\n .wifi path{ animation: wave 1.6s ease-in-out infinite; transform-origin:center; }\r\n .wifi path:nth-child(1){ opacity:.35; animation-delay:.05s;}\r\n .wifi path:nth-child(2){ opacity:.55; animation-delay:.12s;}\r\n .wifi path:nth-child(3){ opacity:.75; animation-delay:.2s;}\r\n .wifi circle{ opacity:.9; animation: blink 1.2s ease-in-out infinite; }\r\n\r\n @keyframes wave{\r\n 0%,100%{ transform: scale(1); }\r\n 50%{ transform: scale(1.06); }\r\n }\r\n @keyframes blink{\r\n 0%,100%{ opacity:.9; }\r\n 50%{ opacity:.45; }\r\n }\r\n\r\n .actions{\r\n display:flex;\r\n flex-wrap:wrap;\r\n gap: 10px;\r\n margin-top: 18px;\r\n }\r\n\r\n .btn{\r\n appearance:none;\r\n border:0;\r\n border-radius: 14px;\r\n padding: 11px 14px;\r\n cursor:pointer;\r\n text-decoration:none;\r\n display:inline-flex;\r\n align-items:center;\r\n gap:10px;\r\n font-weight: 650;\r\n font-size: 14.5px;\r\n letter-spacing: .01em;\r\n transition: transform .12s ease, filter .12s ease, background .2s ease, border-color .2s ease;\r\n user-select:none;\r\n }\r\n .btn:active{ transform: translateY(1px) scale(.99); }\r\n\r\n .primary{\r\n background: #ffffff;\r\n color: #0b0f19;\r\n box-shadow: 0 8px 24px rgba(255,255,255,.12);\r\n }\r\n .primary:hover{ filter: brightness(0.98); }\r\n\r\n .ghost{\r\n background: rgba(255,255,255,.08);\r\n color: var(--text);\r\n border: 1px solid rgba(255,255,255,.14);\r\n }\r\n .ghost:hover{\r\n background: rgba(255,255,255,.10);\r\n border-color: rgba(255,255,255,.18);\r\n }\r\n\r\n .hint{\r\n margin-top: 14px;\r\n display:flex;\r\n gap:10px;\r\n flex-wrap:wrap;\r\n align-items:center;\r\n color: var(--muted2);\r\n font-size: 13px;\r\n }\r\n\r\n .kbd{\r\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, \"Liberation Mono\", monospace;\r\n font-size: 12px;\r\n padding: 4px 8px;\r\n border-radius: 9px;\r\n border:1px solid rgba(255,255,255,.14);\r\n background: rgba(0,0,0,.20);\r\n color: rgba(255,255,255,.82);\r\n }\r\n\r\n /* Toast */\r\n .toast{\r\n position: fixed;\r\n left: 50%;\r\n bottom: 18px;\r\n transform: translateX(-50%);\r\n padding: 10px 12px;\r\n border-radius: 999px;\r\n background: rgba(0,0,0,.40);\r\n border:1px solid rgba(255,255,255,.14);\r\n color: rgba(255,255,255,.86);\r\n font-size: 13px;\r\n display:flex;\r\n gap:10px;\r\n align-items:center;\r\n backdrop-filter: blur(10px);\r\n -webkit-backdrop-filter: blur(10px);\r\n box-shadow: 0 10px 40px rgba(0,0,0,.35);\r\n opacity:0;\r\n pointer-events:none;\r\n transition: opacity .22s ease, transform .22s ease;\r\n }\r\n .toast.show{\r\n opacity:1;\r\n transform: translateX(-50%) translateY(-2px);\r\n }\r\n .toast .okdot{\r\n width:10px;height:10px;border-radius:50%;\r\n background: var(--good);\r\n box-shadow: 0 0 0 6px rgba(46,229,157,.10);\r\n }\r\n\r\n /* Reduce motion */\r\n @media (prefers-reduced-motion: reduce){\r\n .blob, .dot, .wifi path, .wifi circle, .card{ animation:none !important; }\r\n .toast{ transition:none; }\r\n }\r\n </style>\r\n</head>\r\n\r\n<body>\r\n <div class=\"blob b1\"></div>\r\n <div class=\"blob b2\"></div>\r\n <div class=\"blob b3\"></div>\r\n\r\n <main class=\"wrap\" role=\"main\">\r\n <section class=\"card\" aria-labelledby=\"offline-title\">\r\n <div class=\"top\">\r\n <div>\r\n <h1 id=\"offline-title\">😵💫 You’re Offline</h1>\r\n <p>\r\n No internet connection detected. Don’t worry — you can retry now,\r\n or go home if it’s already cached. ✨\r\n </p>\r\n </div>\r\n\r\n <div class=\"badge\" aria-live=\"polite\">\r\n <span class=\"dot\" aria-hidden=\"true\"></span>\r\n <span id=\"netText\">Offline mode</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"hero\">\r\n <div class=\"wifi\" aria-hidden=\"true\">\r\n <svg width=\"30\" height=\"30\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path d=\"M2.5 9.2C8.6 3.2 15.4 3.2 21.5 9.2\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n <path d=\"M5.8 12.4C10.1 8.3 13.9 8.3 18.2 12.4\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n <path d=\"M9.2 15.7C11.2 13.9 12.8 13.9 14.8 15.7\" stroke=\"white\" stroke-width=\"2\" stroke-linecap=\"round\"/>\r\n <circle cx=\"12\" cy=\"19\" r=\"1.4\" fill=\"white\"/>\r\n </svg>\r\n </div>\r\n\r\n <div>\r\n <p style=\"margin:0 0 6px;color:rgba(255,255,255,.86);font-weight:650\">\r\n Tip: Your cached pages may still open ⚡\r\n </p>\r\n <p style=\"margin:0;color:rgba(255,255,255,.62)\">\r\n We’ll auto-refresh when you’re back online ✅\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <div class=\"actions\">\r\n <button class=\"btn primary\" id=\"retryBtn\" type=\"button\">\r\n 🔄 Retry\r\n </button>\r\n <a class=\"btn ghost\" href=\"/\" rel=\"noopener\">\r\n 🏠 Go Home\r\n </a>\r\n <button class=\"btn ghost\" id=\"copyBtn\" type=\"button\" title=\"Copy current URL\">\r\n 🔗 Copy URL\r\n </button>\r\n </div>\r\n\r\n <div class=\"hint\">\r\n <span>Quick keys:</span>\r\n <span class=\"kbd\">R</span> Retry\r\n <span class=\"kbd\">H</span> Home\r\n </div>\r\n </section>\r\n </main>\r\n\r\n <div class=\"toast\" id=\"toast\" role=\"status\" aria-live=\"polite\">\r\n <span class=\"okdot\" aria-hidden=\"true\"></span>\r\n <span id=\"toastText\">Back online — reloading…</span>\r\n </div>\r\n\r\n <script>\r\n const retryBtn = document.getElementById(\"retryBtn\");\r\n const copyBtn = document.getElementById(\"copyBtn\");\r\n const netText = document.getElementById(\"netText\");\r\n const toast = document.getElementById(\"toast\");\r\n const toastText= document.getElementById(\"toastText\");\r\n\r\n function showToast(msg){\r\n toastText.textContent = msg;\r\n toast.classList.add(\"show\");\r\n clearTimeout(showToast._t);\r\n showToast._t = setTimeout(() => toast.classList.remove(\"show\"), 2200);\r\n }\r\n\r\n function updateStatus(){\r\n const online = navigator.onLine;\r\n netText.textContent = online ? \"Online\" : \"Offline mode\";\r\n // If it becomes online, reload after tiny delay (feels smoother)\r\n if (online){\r\n showToast(\"✅ Back online — reloading…\");\r\n setTimeout(() => location.reload(), 650);\r\n }\r\n }\r\n\r\n retryBtn.addEventListener(\"click\", () => location.reload());\r\n\r\n copyBtn.addEventListener(\"click\", async () => {\r\n try{\r\n await navigator.clipboard.writeText(location.href);\r\n showToast(\"📋 URL copied!\");\r\n }catch{\r\n // fallback\r\n const ta = document.createElement(\"textarea\");\r\n ta.value = location.href;\r\n document.body.appendChild(ta);\r\n ta.select();\r\n document.execCommand(\"copy\");\r\n ta.remove();\r\n showToast(\"📋 URL copied!\");\r\n }\r\n });\r\n\r\n window.addEventListener(\"online\", updateStatus);\r\n window.addEventListener(\"offline\", updateStatus);\r\n\r\n // Keyboard shortcuts\r\n window.addEventListener(\"keydown\", (e) => {\r\n const k = (e.key || \"\").toLowerCase();\r\n if (k === \"r\") location.reload();\r\n if (k === \"h\") location.href = \"/\";\r\n });\r\n\r\n // initial\r\n updateStatus();\r\n </script>\r\n</body>\r\n</html>`;\r\n}","export function offlineSvgTemplate() {\r\n return `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"1200\" height=\"630\" viewBox=\"0 0 1200 630\">\r\n <rect width=\"1200\" height=\"630\" fill=\"#0b0f19\"/>\r\n <text x=\"80\" y=\"220\" fill=\"#ffffff\" font-size=\"64\" font-family=\"system-ui, -apple-system, Segoe UI, Roboto\">You’re Offline</text>\r\n <text x=\"80\" y=\"300\" fill=\"#cbd5e1\" font-size=\"28\" font-family=\"system-ui, -apple-system, Segoe UI, Roboto\">Please check your connection and try again.</text>\r\n <circle cx=\"1040\" cy=\"220\" r=\"90\" fill=\"rgba(255,255,255,0.08)\"/>\r\n <path d=\"M980 220c40-40 120-40 160 0\" stroke=\"#fff\" stroke-width=\"10\" fill=\"none\" opacity=\"0.6\"/>\r\n <path d=\"M1010 250c25-25 75-25 100 0\" stroke=\"#fff\" stroke-width=\"10\" fill=\"none\" opacity=\"0.6\"/>\r\n <circle cx=\"1060\" cy=\"290\" r=\"10\" fill=\"#fff\" opacity=\"0.7\"/>\r\n</svg>`;\r\n}","import type { OfflineKitBuildOptions } from \"../types\";\r\n\r\nconst js = (v: unknown) => JSON.stringify(v);\r\n\r\nexport function buildServiceWorkerJS(options: Required<OfflineKitBuildOptions>) {\r\n const { cacheName, offlinePage, offlineImage } = options;\r\n\r\n return `/* offline-page-kit service worker (minimal) */\r\nconst CACHE_NAME = ${js(cacheName)};\r\nconst OFFLINE_PAGE = ${js(offlinePage)};\r\nconst OFFLINE_IMAGE = ${js(offlineImage)};\r\n\r\nself.addEventListener(\"install\", (event) => {\r\n event.waitUntil((async () => {\r\n const cache = await caches.open(CACHE_NAME);\r\n\r\n // ✅ Do not let one 404 kill the install\r\n await Promise.allSettled([\r\n cache.add(OFFLINE_PAGE),\r\n cache.add(OFFLINE_IMAGE),\r\n ]);\r\n\r\n await self.skipWaiting();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"activate\", (event) => {\r\n event.waitUntil((async () => {\r\n await self.clients.claim();\r\n })());\r\n});\r\n\r\nself.addEventListener(\"fetch\", (event) => {\r\n const req = event.request;\r\n\r\n // ✅ Offline fallback only for page navigations\r\n if (req.mode === \"navigate\") {\r\n event.respondWith((async () => {\r\n try {\r\n return await fetch(req);\r\n } catch {\r\n const cache = await caches.open(CACHE_NAME);\r\n return (await cache.match(OFFLINE_PAGE)) || new Response(\"Offline\", { status: 503 });\r\n }\r\n })());\r\n }\r\n});\r\n`;\r\n}"],"mappings":";;;AACA,OAAOA,WAAU;;;ACDjB,OAAO,QAAQ;AACf,OAAO,UAAU;AAEV,SAAS,UAAU,GAAW;AACjC,KAAG,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AACvC;AAEO,SAAS,cAAc,UAAkB,SAAiB;AAC7D,YAAU,KAAK,QAAQ,QAAQ,CAAC;AAChC,KAAG,cAAc,UAAU,SAAS,MAAM;AAC9C;AAEO,SAAS,OAAO,UAAkB;AACrC,MAAI;AAAE,OAAG,WAAW,QAAQ;AAAG,WAAO;AAAA,EAAM,QAAQ;AAAE,WAAO;AAAA,EAAO;AACxE;AAEO,SAAS,UAAU,MAAgB;AACtC,QAAM,IAAI,oBAAI,IAAoB;AAElC,QAAM,OAAO,CAAC,MAAc,EAAE,QAAQ,MAAM,EAAE,EAAE,YAAY;AAE5D,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AAClC,UAAM,IAAI,KAAK,CAAC;AAChB,QAAI,CAAC,EAAE,WAAW,IAAI,EAAG;AAEzB,UAAM,SAAS,EAAE,MAAM,CAAC;AACxB,UAAM,MAAM,KAAK,MAAM;AAEvB,UAAM,QAAQ,KAAK,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,WAAW,IAAI,IAAI,KAAK,EAAE,CAAC,IAAI;AACzE,MAAE,IAAI,KAAK,KAAK;AAAA,EACpB;AAEA,SAAO;AAAA,IACH,IAAI,MAAc;AACd,aAAO,EAAE,IAAI,KAAK,IAAI,CAAC;AAAA,IAC3B;AAAA,EACJ;AACJ;AAEO,SAAS,UAAU,GAAuB;AAC7C,UAAQ,KAAK,IACR,MAAM,GAAG,EACT,IAAI,OAAK,EAAE,KAAK,CAAC,EACjB,OAAO,OAAO;AACvB;;;AC5CO,SAAS,oBAAoB,QAAQ,kBAAkB;AAC5D,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA,WAKE,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA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","names":["path","options","path"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "offline-page-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.3",
|
|
4
4
|
"description": "Framework-agnostic offline page + service worker generator (TypeScript)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -11,10 +11,10 @@
|
|
|
11
11
|
"homepage": "https://devcrack.dev",
|
|
12
12
|
"repository": {
|
|
13
13
|
"type": "git",
|
|
14
|
-
"url": "git+https://github.com/
|
|
14
|
+
"url": "git+https://github.com/kuhelahmed2024/offline-page-kit.git"
|
|
15
15
|
},
|
|
16
16
|
"bugs": {
|
|
17
|
-
"url": "https://github.com/
|
|
17
|
+
"url": "https://github.com/kuhelahmed2024/offline-page-kit/issues"
|
|
18
18
|
},
|
|
19
19
|
"funding": {
|
|
20
20
|
"type": "individual",
|