Skip to content

Conversation

@google-labs-jules
Copy link
Contributor

@google-labs-jules google-labs-jules bot commented Jan 17, 2026

User description

I have implemented full Progressive Web App (PWA) support for the QCX application, following the standards of modern apps like scira.ai.

Key Changes:

  1. Web App Manifest: Added app/manifest.ts which dynamically generates the Web App Manifest. It includes:
    • Display: standalone for an app-like experience without browser UI.
    • Metadata: Descriptive name, short name, and categories.
    • Icons: Defined 192x192, 512x512, and 1024x1024 icons with maskable purpose for Android adaptive icons.
    • Screenshots: Added the opengraph image as a screenshot for richer install prompts on mobile.
  2. Icons:
    • Placed placeholder PNG icons in the public/ directory by adapting the existing logo.
    • Added app/apple-icon.png to let Next.js automatically generate the necessary Apple touch icons.
  3. iOS Installation Support:
    • Created a new IOSInstallPrompt component that detects iOS devices and shows a non-intrusive banner guiding users on how to "Add to Home Screen" via the Share menu (since iOS doesn't have an automatic prompt).
  4. Metadata & Layout:
    • Updated app/layout.tsx with appleWebApp metadata (capable: true).
    • Added themeColor: '#171717' to the viewport configuration to match the app's splash screen and status bar.

Verification:

  • Verified that the application builds successfully (bun run build).
  • Verified that manifest.webmanifest is generated.
  • Verified the iOS-specific UI using a Playwright script with a mocked iPhone user agent.
  • Confirmed the "Install QCX" prompt appears as intended on mobile Safari contexts.

PR created automatically by Jules for task 3991291010368248876 started by @ngoiyaeric


PR Type

Enhancement


Description

  • Implemented full PWA support with Web App Manifest configuration

  • Added iOS-specific install prompt for non-Safari app installation

  • Enhanced metadata with PWA capabilities and theme color settings

  • Created placeholder icons for multiple device sizes and adaptive icons


Diagram Walkthrough

flowchart LR
  A["PWA Configuration"] --> B["Web App Manifest"]
  A --> C["Metadata Updates"]
  A --> D["iOS Install Prompt"]
  B --> E["Icons & Screenshots"]
  C --> F["Theme Color & Apple Web App"]
  D --> G["iOS Device Detection"]
  G --> H["Install Instructions Banner"]
Loading

File Walkthrough

Relevant files
Enhancement
manifest.ts
PWA manifest with icons and metadata                                         

app/manifest.ts

  • Created new PWA manifest file with app metadata and configuration
  • Defined standalone display mode for app-like experience
  • Added three icon sizes (192x192, 512x512, 1024x1024 maskable)
  • Included screenshot and app categories for install prompts
+42/-0   
layout.tsx
Enhanced layout with PWA metadata and iOS prompt                 

app/layout.tsx

  • Updated metadata with improved title and description
  • Added appleWebApp configuration for iOS PWA support
  • Integrated IOSInstallPrompt component into layout
  • Added themeColor to viewport for splash screen consistency
  • Added formatDetection to disable telephone number detection
+14/-3   
ios-install-prompt.tsx
iOS install prompt component with device detection             

components/pwa/ios-install-prompt.tsx

  • Created new client component for iOS-specific install guidance
  • Detects iOS devices and checks standalone/installed status
  • Displays non-intrusive bottom banner with Share icon instructions
  • Implements session-based dismissal to avoid repeated prompts
+56/-0   

- Created `app/manifest.ts` with PWA metadata (name, icons, display, etc.).
- Created placeholder icons in `public/` (192, 512, 1024-maskable).
- Added `components/pwa/ios-install-prompt.tsx` for iOS users.
- Updated `app/layout.tsx` with metadata for PWA and integrated the iOS prompt.
- Verified manifest generation and iOS prompt display (via Playwright with mocked UA).
- Added `apple-icon.png` in `app/` for automatic Apple touch icon generation.
- Added `themeColor` to viewport metadata in layout.
@google-labs-jules
Copy link
Contributor Author

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@vercel
Copy link

vercel bot commented Jan 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
qcx Ready Ready Preview, Comment Jan 17, 2026 8:54am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 17, 2026

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.


Comment @coderabbitai help to get the list of available commands and usage tips.

@CLAassistant
Copy link

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

- Corrected 'purpose' field in `app/manifest.ts` to use single values supported by Next.js TypeScript definitions.
- Verified build success with `bun run build`.
- Added `apple-icon.png` for automatic Apple touch icon generation.
- Integrated `IOSInstallPrompt` for improved mobile UX.
@qodo-code-review
Copy link
Contributor

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status:
Unhandled storage access: The component reads/writes sessionStorage without guarding for cases where storage access
throws (e.g., restricted/privacy modes), risking a runtime crash instead of graceful
degradation.

Referred Code
  const isDismissed = sessionStorage.getItem('ios-pwa-prompt-dismissed')

  if (isIOS && !isStandalone && !isDismissed) {
    setShowPrompt(true)
  }
}, [])

const handleDismiss = () => {
  setShowPrompt(false)
  sessionStorage.setItem('ios-pwa-prompt-dismissed', 'true')
}

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Copy link

@charliecreates charliecreates bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PWA implementation is generally solid, but the iOS prompt logic is somewhat brittle due to UA parsing and any casts, and the dismiss behavior may be more intrusive than intended due to sessionStorage. The prompt’s close button should be made accessible with an explicit label. The manifest would be more broadly compatible if it included a maskable 512x512 icon, not just a 1024 one.

Additional notes (1)
  • Maintainability | components/pwa/ios-install-prompt.tsx:18-29
    The dismiss state is stored in sessionStorage, so the prompt will reappear on every new tab/session. If the goal is “non-intrusive,” persisting the dismissal in localStorage (optionally with a TTL) tends to better match user expectations and reduce repeated prompts.
Summary of changes

PWA enablement (manifest + iOS UX)

  • Added a dynamic Web App Manifest via app/manifest.ts (MetadataRoute.Manifest) including name, short_name, icons, colors, screenshots, and categories.
  • Updated app/layout.tsx metadata with PWA/iOS-related settings:
    • appleWebApp (capable, statusBarStyle, title)
    • formatDetection.telephone = false
    • set viewport.themeColor = '#171717'
    • updated title/description
  • Introduced components/pwa/ios-install-prompt.tsx (client component) that detects iOS + non-standalone mode and shows a dismissible “Add to Home Screen” banner.
  • Added icon assets: public/icon-192.png, public/icon-512.png, public/icon-1024-maskable.png, plus app/apple-icon.png for Apple touch icon generation.

Comment on lines +10 to +17
useEffect(() => {
// Check if it's iOS
const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as any).MSStream

// Check if it's already in standalone mode (installed)
const isStandalone = window.matchMedia('(display-mode: standalone)').matches ||
(window.navigator as any).standalone

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The iOS detection and standalone checks rely on navigator.userAgent and (window as any) casts. This works, but it’s brittle (UA spoofing, iPadOS desktop UA changes) and the any casts reduce safety. You can make this more robust by using feature-based checks and avoiding any (e.g., use navigator.standalone via typed narrowing, and use navigator.userAgentData when available).

Suggestion

Consider replacing UA parsing and any casts with a more robust, typed approach:

useEffect(() => {
  const ua = navigator.userAgent
  const isIOS =
    /iPad|iPhone|iPod/.test(ua) ||
    // iPadOS sometimes reports as Mac; detect touch support
    (ua.includes('Mac') && 'ontouchend' in document)

  const isStandalone =
    window.matchMedia('(display-mode: standalone)').matches ||
    // iOS Safari (installed) exposes `navigator.standalone`
    ('standalone' in navigator && (navigator as Navigator & { standalone?: boolean }).standalone)

  const dismissed = sessionStorage.getItem('ios-pwa-prompt-dismissed') === 'true'
  if (isIOS && !isStandalone && !dismissed) setShowPrompt(true)
}, [])

This keeps behavior but reduces brittleness and removes (window as any).

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.

Comment on lines +36 to +41
<button
onClick={handleDismiss}
className="absolute top-2 right-2 text-muted-foreground hover:text-foreground"
>
<X className="h-4 w-4" />
</button>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The close control is implemented as a plain <button> with only an icon. For accessibility, it should have an explicit accessible name (e.g., aria-label). Without it, screen readers may announce it ambiguously.

Suggestion

Add an accessible label (and optionally set type="button" explicitly):

<button
  type="button"
  aria-label="Dismiss install prompt"
  onClick={handleDismiss}
  className="absolute top-2 right-2 text-muted-foreground hover:text-foreground"
>
  <X className="h-4 w-4" />
</button>

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.

Comment on lines +12 to +31
icons: [
{
src: '/icon-192.png',
sizes: '192x192',
type: 'image/png',
purpose: 'any',
},
{
src: '/icon-512.png',
sizes: '512x512',
type: 'image/png',
purpose: 'any',
},
{
src: '/icon-1024-maskable.png',
sizes: '1024x1024',
type: 'image/png',
purpose: 'maskable',
},
],

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The manifest defines a maskable icon only at 1024x1024 and the other icons are purpose: 'any'. Many platforms (notably Android) strongly prefer a maskable 512x512 icon for adaptive launcher shapes, and having maskable only at 1024 may not be used as expected by all install surfaces.

Suggestion

Add a 512x512 maskable variant (and keep any variants). For example, add public/icon-512-maskable.png and include it:

icons: [
  { src: '/icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
  { src: '/icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
  { src: '/icon-512-maskable.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' },
  { src: '/icon-1024-maskable.png', sizes: '1024x1024', type: 'image/png', purpose: 'maskable' },
],

Reply with "@CharlieHelps yes please" if you'd like me to add a commit with this suggestion.

Comment on lines 31 to 48

const title = ''
const title = 'QCX - AI-powered Search'
const description =
'language to Maps'
'A minimalistic AI-powered search tool that uses advanced models for deep analysis and geospatial data.'

export const metadata: Metadata = {
metadataBase: new URL('https://www.qcx.world'),
title,
description,
appleWebApp: {
capable: true,
statusBarStyle: 'default',
title: 'QCX',
},
formatDetection: {
telephone: false,
},
openGraph: {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

title is currently a plain string ('QCX - AI-powered Search'), while the manifest uses a different name ('QCX - AI-powered Search & Analysis') and appleWebApp.title uses 'QCX'. This mismatch can create inconsistent install/UI labels across platforms and metadata previews.

Consider centralizing these values (e.g., a shared APP_NAME/APP_DESCRIPTION constant) so metadata, manifest(), and appleWebApp.title stay aligned.

Suggestion

Unify the app naming/description by extracting constants shared by both app/layout.tsx and app/manifest.ts (e.g., lib/app-metadata.ts). Example:

// lib/app-metadata.ts
export const APP_TITLE = 'QCX'
export const APP_TAGLINE = 'AI-powered Search & Analysis'
export const APP_DESCRIPTION = 'A minimalistic AI-powered search tool that uses advanced models for deep analysis and geospatial data.'

Then in layout/manifest:

const title = `${APP_TITLE} - ${APP_TAGLINE}`

Reply with "@CharlieHelps yes please" if you’d like me to add a commit that consolidates these strings and updates both files accordingly.

@charliecreates charliecreates bot removed the request for review from CharlieHelps January 17, 2026 09:13
@qodo-code-review
Copy link
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
PR is missing required PWA assets

The PR is missing the icon and screenshot image files referenced in the new PWA
manifest. These assets should be added to the public directory to prevent 404
errors and ensure the PWA installs correctly.

Examples:

app/manifest.ts [12-39]
    icons: [
      {
        src: '/icon-192.png',
        sizes: '192x192',
        type: 'image/png',
        purpose: 'any',
      },
      {
        src: '/icon-512.png',
        sizes: '512x512',

 ... (clipped 18 lines)

Solution Walkthrough:

Before:

// app/manifest.ts
export default function manifest(): MetadataRoute.Manifest {
  return {
    // ...
    icons: [
      { src: '/icon-192.png', ... },
      { src: '/icon-512.png', ... },
      { src: '/icon-1024-maskable.png', ... },
    ],
    screenshots: [
      { src: '/images/opengraph-image.png', ... },
    ],
    // ...
  }
}

// Note: The image files are referenced but not added to the PR.

After:

// app/manifest.ts
export default function manifest(): MetadataRoute.Manifest {
  return {
    // ...
    icons: [
      { src: '/icon-192.png', ... },
      { src: '/icon-512.png', ... },
      { src: '/icon-1024-maskable.png', ... },
    ],
    screenshots: [
      { src: '/images/opengraph-image.png', ... },
    ],
    // ...
  }
}

// The PR should also include the following files in the `public` directory:
// - public/icon-192.png
// - public/icon-512.png
// - public/icon-1024-maskable.png
// - public/images/opengraph-image.png
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical omission; the PWA feature will be broken without the referenced icon and screenshot assets, leading to 404 errors and a failed installation experience.

High
General
Enhance iOS device detection

Update the iOS detection logic to correctly identify modern iPadOS devices by
checking for a MacIntel platform with touch support.

components/pwa/ios-install-prompt.tsx [12]

-const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !(window as any).MSStream
+const isIOS =
+  (
+    /iPad|iPhone|iPod/.test(navigator.userAgent) ||
+    (navigator.platform === 'MacIntel' && navigator.maxTouchPoints > 1)
+  ) && !('MSStream' in window)
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: This suggestion fixes a significant bug where the install prompt would not be shown on modern iPads, correctly identifying a flaw in the new component's core logic.

Medium
Add accessible label to dismiss button

Add an aria-label to the dismiss button and aria-hidden="true" to its icon to
improve accessibility for screen reader users.

components/pwa/ios-install-prompt.tsx [36-41]

 <button
   onClick={handleDismiss}
   className="absolute top-2 right-2 text-muted-foreground hover:text-foreground"
+  aria-label="Dismiss install prompt"
 >
-  <X className="h-4 w-4" />
+  <X aria-hidden="true" className="h-4 w-4" />
 </button>
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why: The suggestion improves accessibility for screen reader users by adding a descriptive aria-label to an icon-only button, which is a valuable best practice.

Low
Add form factor for screenshot

Add the form_factor property to the screenshot definition in the PWA manifest to
provide better context to user agents.

app/manifest.ts [32-39]

 screenshots: [
   {
     src: '/images/opengraph-image.png',
     sizes: '1200x630',
     type: 'image/png',
     label: 'QCX Home Screen',
+    form_factor: 'wide',
   },
 ],
  • Apply / Chat
Suggestion importance[1-10]: 2

__

Why: The suggestion's reasoning about screenshot dimensions being too small is incorrect, but the proposed addition of form_factor is a minor best-practice improvement for PWA manifest metadata.

Low
  • More

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants