Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions docs/superpowers/specs/2026-07-05-pwa-tab-restructure-design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# OpenProof PWA Tab Restructure

Date: 2026-07-05
Status: Approved

## Problem

1. WagmiProviderNotFoundError on Create/Verify pages in PWA mode. `WalletProvider` lives in `AppShell` but PWA mode uses `PwaShell` which lacks it.
2. `/app` homepage has a splash screen + action cards + history + testnet notice — too much for a focused app.
3. About / Privacy / Terms are only accessible on the website, not inside the PWA.
4. Each PWA tab should open directly to its functional content.

## Solution

### 1. Move WalletProvider to root layout

`WalletProvider` moves from `AppShell` → root `layout.tsx`, wrapping `ConditionalShell`. Removed from `AppShell` to prevent nesting. Fixes wagmi context for all routes in both PWA and browser mode.

### 2. Tab routing

| Tab | Route | Content |
|-----|-------|---------|
| **Create** | `/app` | App title + one-line summary + full create-proof flow |
| **Verify** | `/app/verify` | Full verify-proof flow |
| **History** | `/app/history` | Proof history + bundle proofs + testnet notice |
| **More** | `/app/more` | About, Privacy, Terms, Docs links |

### 3. Page changes

- **`/app/page.tsx`**: Remove `AppSplash`, remove action cards. Show "Create Proof" title + summary + redirect to or embed the create flow from `/create`.
- **`/app/history/page.tsx`**: Add bundle proofs card + testnet notice (moved from `/app/page.tsx`).
- **`/app/more/page.tsx`**: Expand with proper sections linking to About, Privacy, Terms, Docs page content.
- **`/app/verify/page.tsx`**: Currently redirects to `/verify`. Change to embed the full verify flow from `/verify`.

### 4. Wallet provider architecture

```
Root layout
├── Script (theme)
├── Script (SW)
├── WalletProvider ← MOVED HERE
│ └── ThemeProvider
│ └── ConditionalShell
│ ├── PwaShell (PWA mode, all routes)
│ └── AppShell (browser mode, non-/app routes)
```

### 5. No layout duplication

- `/create` and `/verify` standalone pages keep working via their existing layout chain.
- `/app/*` PWA routes use `PwaShell` via `ConditionalShell`.
- Shared wallet context at root level means components using `useAccount()`, `usePublicClient()`, etc. work everywhere.

### 6. Desktop and mobile parity

Same structure on both. No splash screen. Each tab opens directly to its functional content.
2 changes: 1 addition & 1 deletion public/kovina-wordmark-white.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion public/kovina-wordmark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions public/scripts/sw-register.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
if ('serviceWorker' in navigator && location.hostname !== 'localhost') {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').catch(function(){});
});
}
5 changes: 5 additions & 0 deletions public/scripts/theme-init.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
(function(){
var t=localStorage.getItem('openproof-theme');
if(!t){t=window.matchMedia('(prefers-color-scheme:light)').matches?'light':'dark'}
document.documentElement.setAttribute('data-theme',t);
})();
48 changes: 48 additions & 0 deletions src/app/app/history/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,61 @@
"use client";

import Link from "next/link";
import { FileBox, ShieldCheck } from "lucide-react";
import { ProofHistory } from "@/components/proof-history";

export default function AppHistoryPage() {
return (
<div className="space-y-8">
{/* Section header */}
<div>
<h1 className="text-2xl font-black tracking-tight text-text-primary">
History
</h1>
<p className="mt-1.5 text-sm text-text-secondary">
Proofs and verifications you&apos;ve completed in this browser.
</p>
</div>

{/* Registered proofs */}
<ProofHistory title="Registered proofs" type="registered" />

{/* Verified proofs */}
<ProofHistory title="Verified proofs" type="verified" />

{/* Bundle proofs CTA */}
<Link
href="/app"
className="flex items-center gap-4 rounded-2xl border border-border-default bg-bg-surface p-5 transition hover:border-accent/50"
>
<div className="flex size-12 shrink-0 items-center justify-center rounded-xl bg-accent/10">
<FileBox className="size-6 text-accent" />
</div>
<div>
<p className="font-bold text-text-primary">Bundle proofs</p>
<p className="mt-0.5 text-sm text-text-secondary">
Register multiple files as a single combined proof
</p>
</div>
</Link>

{/* Testnet notice */}
<div className="rounded-2xl border border-border-default bg-bg-surface p-5">
<div className="flex items-start gap-3">
<ShieldCheck className="mt-0.5 size-5 shrink-0 text-text-muted" />
<div>
<p className="text-sm font-semibold text-text-primary">
Base Sepolia testnet
</p>
<p className="mt-1 text-xs leading-relaxed text-text-secondary">
OpenProof runs on Base Sepolia — a test network. Proofs here are
for experimentation and verification. No real value is involved.
</p>
</div>
</div>
</div>

{/* Footer spacer for bottom nav on mobile */}
<div className="h-20 md:hidden" />
</div>
);
Expand Down
9 changes: 9 additions & 0 deletions src/app/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
"use client";

import { usePwaMode } from "@/lib/use-pwa-mode";
import { PwaShell } from "@/components/pwa-shell";

export default function AppLayout({ children }: { children: React.ReactNode }) {
const { isPwa } = usePwaMode();

// In installed PWA mode, ConditionalShell already wraps everything in PwaShell.
// Only apply PwaShell here when browsing /app in a regular browser tab.
if (isPwa) {
return <>{children}</>;
}

return <PwaShell>{children}</PwaShell>;
}
25 changes: 25 additions & 0 deletions src/app/app/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* Loading boundary for the PWA app route group.
*
* Shows lightweight skeleton cards during route transitions so the user
* never sees a blank flash between /app pages.
*/
export default function AppLoading() {
return (
<div className="space-y-6 animate-pulse">
{/* Skeleton for quick-action grid */}
<div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
<div className="h-[72px] rounded-2xl bg-bg-surface-muted" />
<div className="h-[72px] rounded-2xl bg-bg-surface-muted" />
</div>

{/* Skeleton for section header */}
<div className="h-5 w-32 rounded bg-bg-surface-muted" />

{/* Skeleton for list items */}
{Array.from({ length: 3 }).map((_, i) => (
<div key={i} className="h-16 rounded-2xl bg-bg-surface-muted" />
))}
</div>
);
}
118 changes: 87 additions & 31 deletions src/app/app/more/page.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,109 @@
"use client";

import Link from "next/link";
import { ExternalLink, ShieldCheck, BookOpen } from "lucide-react";
import { ExternalLink, ShieldCheck, BookOpen, FileText, Lock, Scale } from "lucide-react";

export default function AppMorePage() {
const links = [
{
href: "/about",
icon: BookOpen,
label: "About OpenProof",
desc: "Mission, philosophy, and how it works",
},
{
href: "/docs",
icon: FileText,
label: "Documentation",
desc: "Guides, architecture, and API references",
},
{
href: "/privacy",
icon: Lock,
label: "Privacy Policy",
desc: "How your data is handled",
},
{
href: "/terms",
icon: Scale,
label: "Terms of Service",
desc: "Rules and legal stuff",
},
];

return (
<div className="space-y-6">
<h1 className="text-xl font-bold">More</h1>
{/* Section header */}
<div>
<h1 className="text-2xl font-black tracking-tight text-text-primary">
More
</h1>
<p className="mt-1.5 text-sm text-text-secondary">
Information and resources about OpenProof.
</p>
</div>

{/* Links */}
<div className="space-y-3">
<Link
href="/about"
className="flex items-center gap-4 rounded-2xl border border-border-default bg-bg-surface p-4 transition hover:border-accent/50"
>
<BookOpen className="size-5 text-accent" />
<span className="font-semibold">About OpenProof</span>
</Link>

<Link
href="/docs"
className="flex items-center gap-4 rounded-2xl border border-border-default bg-bg-surface p-4 transition hover:border-accent/50"
>
<ShieldCheck className="size-5 text-accent" />
<span className="font-semibold">Documentation</span>
</Link>

<a
href="https://github.com/sparshsam/openproof"
target="_blank"
rel="noreferrer"
className="flex items-center gap-4 rounded-2xl border border-border-default bg-bg-surface p-4 transition hover:border-accent/50"
>
<span className="font-semibold">GitHub</span>
<ExternalLink className="ml-auto size-4 text-text-muted" />
</a>
{links.map(({ href, icon: Icon, label, desc }) => (
<Link
key={href}
href={href}
className="flex items-center gap-4 rounded-2xl border border-border-default bg-bg-surface p-4 transition hover:border-accent/50"
>
<div className="flex size-12 shrink-0 items-center justify-center rounded-xl bg-accent/10">
<Icon className="size-6 text-accent" />
</div>
<div>
<p className="font-semibold text-text-primary">{label}</p>
{desc ? (
<p className="mt-0.5 text-sm text-text-secondary">{desc}</p>
) : null}
</div>
</Link>
))}
</div>

{/* GitHub */}
<a
href="https://github.com/sparshsam/openproof"
target="_blank"
rel="noreferrer"
className="flex items-center gap-4 rounded-2xl border border-border-default bg-bg-surface p-4 transition hover:border-accent/50"
>
<div className="flex size-12 shrink-0 items-center justify-center rounded-xl bg-accent/10">
<ExternalLink className="size-6 text-accent" />
</div>
<div className="flex-1">
<p className="font-semibold text-text-primary">GitHub</p>
<p className="mt-0.5 text-sm text-text-secondary">
Source code, issues, and contribution guide
</p>
</div>
<ExternalLink className="size-4 text-text-muted shrink-0" />
</a>

{/* Testnet notice */}
<div className="rounded-2xl border border-border-default bg-bg-surface p-4">
<p className="text-xs text-text-secondary leading-relaxed">
OpenProof runs on <strong className="text-text-primary">Base Sepolia</strong> (testnet).
All proofs are for experimental and verification purposes only.
</p>
<div className="flex items-start gap-3">
<ShieldCheck className="mt-0.5 size-5 shrink-0 text-text-muted" />
<div>
<p className="text-sm font-semibold text-text-primary">
Base Sepolia testnet
</p>
<p className="mt-1 text-xs leading-relaxed text-text-secondary">
OpenProof runs on Base Sepolia — a test network.
All proofs are for experimental and verification purposes only.
</p>
</div>
</div>
</div>

<p className="text-center text-xs text-text-muted">
OpenProof v0.9.3 &middot; AGPL-3.0
</p>

{/* Footer spacer for bottom nav on mobile */}
<div className="h-20 md:hidden" />
</div>
);
Expand Down
Loading
Loading