Skip to content

Commit bad00f2

Browse files
committed
feat: PWA install modal with cross-browser fallback
- Early beforeinstallprompt capture in <head> script - Always-visible gradient install pill with pulse animation - Install modal with app icon, Install/Skip, animated entrance - iOS/Safari/generic fallback instructions when native prompt unavailable - Mobile hamburger menu Install App button - Auto-hide install UI when running as standalone PWA - appinstalled event cleanup
1 parent 6fa3198 commit bad00f2

5 files changed

Lines changed: 378 additions & 13 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,7 @@ TextAgent has undergone significant evolution since its inception. What started
543543

544544
| Date | Commits | Feature / Update |
545545
|------|---------|-----------------:|
546+
| **2026-03-30** | | 📲 **PWA Install UX** — redesigned install flow with always-visible gradient pill button (pulse animation), polished install modal (app icon, description, Install/Skip), early `beforeinstallprompt` capture in `<head>` script to prevent timing race, browser-aware fallback instructions (iOS: Share → Add to Home Screen, Safari: File → Add to Dock), mobile hamburger menu "Install App" button, auto-hide when running in standalone PWA mode |
546547
| **2026-03-30** | | 📱 **Mobile AI Panel Optimization** — maximized chat area on mobile (≤767px) by hiding secondary UI elements by default; removed duplicate model badge and "AI Assistant" text from header; search toggle restyled as compact icon-only button (turns solid green when enabled); model selector merged into chat input bar as compact gradient icon; quick actions and search providers now independently toggleable via separate grid and globe buttons; mobile dark mode toggle fixed with icon/label sync; ~60% header height reduction on mobile |
547548
| **2026-03-30** | | 📂 **File Import Expansion** — added 15 new file extensions to the import pipeline: `.txt`, `.text`, `.log`, `.rst`, `.ini`, `.conf`, `.cfg`, `.env`, `.properties` as plain text; `.yaml`/`.yml` wrapped in yaml code block; `.toml` wrapped in toml code block; `.tsv` parsed as tab-separated Markdown table; updated file input `accept` attribute with all new extensions and MIME types; updated dropzone label and error toast; total supported extensions now 25 |
548549
| **2026-03-29** | | 🔑 **Secure Session Editing** — cryptographic Edit Key (`ek`) system for collaborative editing of shared documents; 24-char random edit key hashed via SHA-256 and stored as `ekHash` in Firestore; write-token encrypted with edit key via AES-GCM stored as `eWt`; editor links (`&ek=<token>`) grant write access without exposing raw write-tokens; verification in both compact (`#s=`) and secure (`#id=&secure=1`) share paths; edit mode bypasses form/quiz access gate; auto-save preserves `ekHash`/`eWt` fields; "Editor Link" section in share result modal with purple badge; "Copy All Links" includes editor link; email/download credentials include editor link; Firestore rules updated for new fields |
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# PWA Install UX — Enhanced Install Prompt with Modal & Cross-Browser Support
2+
3+
- Early `beforeinstallprompt` capture via inline `<head>` script before any modules load
4+
- PWA install button moved outside toolbar overflow area to always-visible pill position
5+
- Gradient-styled install pill with pulse animation (3 cycles) to draw attention
6+
- New install modal with app icon, description, Install/Skip buttons, and animated entrance
7+
- Mobile hamburger menu now includes "Install App" button with gradient accent styling
8+
- iOS/Safari fallback: when native prompt unavailable, shows manual Share → Add to Home Screen instructions
9+
- Auto-hides install buttons when app is already running in standalone PWA mode
10+
- Desktop Safari fallback: shows File → Add to Dock instruction
11+
- Overlay click and close button dismiss the install modal
12+
- `appinstalled` event cleans up all install UI elements
13+
14+
---
15+
16+
## Summary
17+
Redesigned the PWA install experience with a prominent always-visible install pill, a polished install modal with browser-aware fallback instructions, and early prompt capture to prevent timing issues.
18+
19+
---
20+
21+
## 1. Early Prompt Capture
22+
**Files:** `index.html`
23+
**What:** Added an inline `<script>` in `<head>` that captures the `beforeinstallprompt` event and stores it on `window.__pwaPrompt` before any modules load.
24+
**Impact:** Eliminates the race condition where the browser fires the event before `app-init.js` has registered its listener.
25+
26+
## 2. Always-Visible Install Pill
27+
**Files:** `index.html`, `css/header.css`
28+
**What:** Moved the install button from inside the toolbar button group (which could be hidden by overflow) to a standalone position outside the toolbar. Styled as a gradient pill with pulse animation.
29+
**Impact:** Users can always see and click the install button regardless of toolbar width.
30+
31+
## 3. Install Modal with Fallback Instructions
32+
**Files:** `index.html`, `js/app-init.js`, `css/header.css`
33+
**What:** Created a centered modal overlay with app icon, description, Install/Skip actions, and browser-specific manual installation instructions for iOS (Share → Add to Home Screen), Safari (File → Add to Dock), and generic browsers.
34+
**Impact:** Users on non-Chromium browsers (where `beforeinstallprompt` isn't available) get clear, actionable instructions instead of a silent failure.
35+
36+
## 4. Mobile Menu Install Button
37+
**Files:** `index.html`, `css/header.css`, `js/app-init.js`
38+
**What:** Added "Install App" button to the mobile hamburger menu with gradient accent styling. Clicking it closes the menu and opens the install modal.
39+
**Impact:** Mobile users have a discoverable install path in the mobile navigation menu.
40+
41+
## 5. Standalone Detection & Cleanup
42+
**Files:** `js/app-init.js`
43+
**What:** Added `window.matchMedia('(display-mode: standalone)')` and `navigator.standalone` checks to auto-hide install buttons when the app is already installed. The `appinstalled` event also cleans up all UI.
44+
**Impact:** Already-installed users don't see unnecessary install prompts.
45+
46+
---
47+
48+
## Files Changed (3 total)
49+
50+
| File | Lines Changed | Type |
51+
|------|:---:|------|
52+
| `index.html` | +30 −4 | Early prompt capture, install modal HTML, mobile menu button, button repositioning |
53+
| `js/app-init.js` | +80 −12 | Modal logic, browser detection, standalone check, event wiring |
54+
| `css/header.css` | +207 −0 | Install pill, install modal, mobile button, animations, dark mode |

css/header.css

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,11 +848,217 @@ a:focus {
848848
font-size: 1.5rem;
849849
}
850850

851+
/* ========================================
852+
PWA INSTALL BUTTON
853+
======================================== */
854+
855+
.pwa-install-pill {
856+
background: linear-gradient(135deg, var(--accent-color), #2ea043) !important;
857+
color: #fff !important;
858+
border-radius: 100px !important;
859+
padding: 5px 14px !important;
860+
font-size: 0.82rem !important;
861+
font-weight: 600 !important;
862+
gap: 5px;
863+
border: none !important;
864+
box-shadow: 0 2px 8px rgba(88, 166, 255, 0.3);
865+
transition: all 0.25s ease;
866+
animation: pwa-pulse 2.5s ease-in-out 3;
867+
flex-shrink: 0;
868+
}
869+
870+
.pwa-install-pill:hover {
871+
transform: translateY(-1px);
872+
box-shadow: 0 4px 16px rgba(88, 166, 255, 0.45) !important;
873+
background: linear-gradient(135deg, #79c0ff, #3fb950) !important;
874+
}
875+
876+
@keyframes pwa-pulse {
877+
0%, 100% { box-shadow: 0 2px 8px rgba(88, 166, 255, 0.3); }
878+
50% { box-shadow: 0 2px 16px rgba(88, 166, 255, 0.55); }
879+
}
880+
881+
/* Mobile menu install button — accent highlight */
882+
#mobile-pwa-install-btn {
883+
background: linear-gradient(135deg, var(--accent-color), #2ea043);
884+
color: #fff !important;
885+
border-radius: 8px;
886+
font-weight: 600;
887+
margin-top: 4px;
888+
}
889+
890+
#mobile-pwa-install-btn:hover {
891+
background: linear-gradient(135deg, #79c0ff, #3fb950);
892+
}
893+
894+
/* ========================================
895+
PWA INSTALL INSTRUCTIONS MODAL
896+
======================================== */
897+
898+
.pwa-install-modal-overlay {
899+
position: fixed;
900+
inset: 0;
901+
z-index: 10000;
902+
display: flex;
903+
align-items: center;
904+
justify-content: center;
905+
background: rgba(0, 0, 0, 0.55);
906+
backdrop-filter: blur(6px);
907+
-webkit-backdrop-filter: blur(6px);
908+
animation: pwa-modal-fadein 0.2s ease;
909+
}
910+
911+
@keyframes pwa-modal-fadein {
912+
from { opacity: 0; }
913+
to { opacity: 1; }
914+
}
915+
916+
.pwa-install-modal {
917+
position: relative;
918+
width: 90%;
919+
max-width: 420px;
920+
background: var(--bg-color);
921+
border: 1px solid var(--border-color);
922+
border-radius: 18px;
923+
padding: 32px 28px 28px;
924+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
925+
text-align: center;
926+
animation: pwa-modal-slidein 0.25s cubic-bezier(0.34, 1.56, 0.64, 1);
927+
}
928+
929+
@keyframes pwa-modal-slidein {
930+
from { transform: translateY(24px) scale(0.96); opacity: 0; }
931+
to { transform: translateY(0) scale(1); opacity: 1; }
932+
}
933+
934+
.pwa-install-modal-close {
935+
position: absolute;
936+
top: 12px;
937+
right: 12px;
938+
background: transparent;
939+
border: none;
940+
color: var(--text-color);
941+
font-size: 1rem;
942+
cursor: pointer;
943+
padding: 4px 8px;
944+
border-radius: 6px;
945+
opacity: 0.5;
946+
transition: all 0.15s;
947+
}
948+
949+
.pwa-install-modal-close:hover {
950+
opacity: 1;
951+
background: var(--button-hover);
952+
}
953+
954+
.pwa-install-modal-icon {
955+
margin-bottom: 16px;
956+
}
957+
958+
.pwa-install-modal-icon img {
959+
border-radius: 16px;
960+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
961+
}
962+
963+
.pwa-install-modal-title {
964+
font-size: 1.35rem;
965+
font-weight: 700;
966+
color: var(--text-color);
967+
margin: 0 0 8px;
968+
}
969+
970+
.pwa-install-modal-desc {
971+
font-size: 0.9rem;
972+
color: var(--text-color);
973+
opacity: 0.65;
974+
margin: 0 0 20px;
975+
line-height: 1.5;
976+
}
977+
978+
.pwa-install-modal-steps {
979+
text-align: left;
980+
}
981+
982+
.pwa-steps-browser {
983+
font-size: 0.82rem;
984+
color: var(--text-color);
985+
opacity: 0.55;
986+
margin-bottom: 14px;
987+
text-align: center;
988+
}
989+
990+
/* Install / Skip actions row */
991+
.pwa-install-modal-actions {
992+
display: flex;
993+
gap: 12px;
994+
margin-top: 20px;
995+
justify-content: center;
996+
}
997+
998+
.pwa-install-confirm-btn {
999+
display: inline-flex;
1000+
align-items: center;
1001+
gap: 8px;
1002+
padding: 11px 32px;
1003+
font-size: 0.95rem;
1004+
font-weight: 600;
1005+
color: #fff;
1006+
background: linear-gradient(135deg, var(--accent-color), #2ea043);
1007+
border: none;
1008+
border-radius: 100px;
1009+
cursor: pointer;
1010+
transition: all 0.2s ease;
1011+
box-shadow: 0 2px 8px rgba(88, 166, 255, 0.3);
1012+
}
1013+
1014+
.pwa-install-confirm-btn:hover {
1015+
transform: translateY(-1px);
1016+
box-shadow: 0 4px 16px rgba(88, 166, 255, 0.45);
1017+
background: linear-gradient(135deg, #79c0ff, #3fb950);
1018+
}
1019+
1020+
.pwa-install-skip-btn {
1021+
padding: 11px 28px;
1022+
font-size: 0.9rem;
1023+
font-weight: 500;
1024+
color: var(--text-color);
1025+
background: transparent;
1026+
border: 1px solid var(--border-color);
1027+
border-radius: 100px;
1028+
cursor: pointer;
1029+
transition: all 0.2s ease;
1030+
opacity: 0.7;
1031+
}
1032+
1033+
.pwa-install-skip-btn:hover {
1034+
opacity: 1;
1035+
background: var(--button-hover);
1036+
}
1037+
1038+
/* Fallback hint (shown when native prompt isn't available) */
1039+
.pwa-install-hint {
1040+
font-size: 0.85rem;
1041+
color: var(--text-color);
1042+
opacity: 0.7;
1043+
margin: 12px 0 0;
1044+
padding: 10px 14px;
1045+
background: var(--button-hover);
1046+
border-radius: 8px;
1047+
line-height: 1.5;
1048+
text-align: center;
1049+
}
1050+
1051+
.pwa-install-hint i {
1052+
margin-right: 4px;
1053+
opacity: 0.6;
1054+
}
1055+
8511056

8521057
/* ========================================
8531058
TOOLBAR OVERFLOW MENU (kebab ⋮)
8541059
======================================== */
8551060

1061+
8561062
.toolbar-overflow-wrapper {
8571063
position: relative;
8581064
display: flex;

index.html

Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,14 @@
5959
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
6060
<meta name="apple-mobile-web-app-title" content="TextAgent">
6161
<link rel="apple-touch-icon" href="/assets/icons/icon-192x192.png">
62-
62+
<!-- Capture PWA install prompt as early as possible (before any scripts load) -->
63+
<script>
64+
window.__pwaPrompt = null;
65+
window.addEventListener('beforeinstallprompt', function (e) {
66+
e.preventDefault();
67+
window.__pwaPrompt = e;
68+
});
69+
</script>
6370
<!-- Anti-flicker: set theme BEFORE any CSS/JS loads -->
6471
<script>
6572
(function () {
@@ -309,15 +316,15 @@
309316
<button id="theme-toggle" class="tool-button" title="Toggle Dark Mode">
310317
<i class="bi bi-moon"></i>
311318
</button>
312-
<button id="pwa-install-btn" class="tool-button" title="Install TextAgent App" style="display:none">
313-
<i class="bi bi-download"></i> Install
314-
</button>
315319
</div>
316320
<button id="toolbar-overflow-btn" class="tool-button toolbar-overflow-toggle"
317321
title="More tools">
318322
<i class="bi bi-three-dots-vertical"></i>
319323
</button>
320324
</div>
325+
<button id="pwa-install-btn" class="tool-button pwa-install-pill" title="Install TextAgent App">
326+
<i class="bi bi-download"></i> Install
327+
</button>
321328
</div>
322329

323330
<!-- Hamburger menu for mobile -->
@@ -424,6 +431,10 @@ <h5>Menu</h5>
424431
<button id="mobile-theme-toggle" class="mobile-menu-item" title="Toggle Dark Mode">
425432
<i class="bi bi-moon me-2"></i> Dark Mode
426433
</button>
434+
435+
<button id="mobile-pwa-install-btn" class="mobile-menu-item" title="Install TextAgent App">
436+
<i class="bi bi-download me-2"></i> Install App
437+
</button>
427438
</div>
428439
</div>
429440

@@ -1130,6 +1141,27 @@ <h4><i class="bi bi-keyboard me-2"></i>Keyboard Shortcuts</h4>
11301141

11311142

11321143

1144+
<!-- PWA Install Modal -->
1145+
<div id="pwa-install-modal" class="pwa-install-modal-overlay" style="display:none">
1146+
<div class="pwa-install-modal">
1147+
<button class="pwa-install-modal-close" id="pwa-install-modal-close" title="Close">
1148+
<i class="bi bi-x-lg"></i>
1149+
</button>
1150+
<div class="pwa-install-modal-icon">
1151+
<img src="/assets/icons/icon-192x192.png" alt="TextAgent" width="72" height="72">
1152+
</div>
1153+
<h3 class="pwa-install-modal-title">Install TextAgent</h3>
1154+
<p class="pwa-install-modal-desc">Works offline, launches instantly, no app store needed.</p>
1155+
<div id="pwa-install-modal-body"></div>
1156+
<div class="pwa-install-modal-actions">
1157+
<button id="pwa-install-confirm-btn" class="pwa-install-confirm-btn">
1158+
<i class="bi bi-download"></i> Install
1159+
</button>
1160+
<button id="pwa-install-skip-btn" class="pwa-install-skip-btn">Skip</button>
1161+
</div>
1162+
</div>
1163+
</div>
1164+
11331165
<script type="module" src="/src/main.js"></script>
11341166

11351167
<!-- PWA Service Worker -->

0 commit comments

Comments
 (0)