The Deckly Developer Bible
Welcome to the internal source of truth. This document provides a high-fidelity guide to the technical architecture, service patterns, and development workflows that power the Deckly "Founder Data Room" experience.
Tech Stack Core
| Layer | Technology | Purpose |
|---|---|---|
| Framework | React 19 + Vite | High-performance SPA with route-level code splitting. |
| Logic | TypeScript | Strict type safety for complex data shapes. |
| Auth/DB | Supabase | Identity, PostgreSQL, and namespaced URL routing logic. |
| Processing | PDF.js (Browser) | Privacy-first, zero-cost client-side rendering. |
| Analytics | PostHog + Custom | Combined deep UX events with aggregated Room visitor signals. |
| Styling | Tailwind CSS + shadcn/ui | Utility-first classes + accessible component primitives. Custom design tokens in App.css. |
Project Architecture
1. File Structure
src/
├── components/
│ ├── common/ # Atomic UI atoms (Buttons, Toggles, Inputs)
│ ├── dashboard/ # Dashboard cards, charts, tables, and split feature sections
│ │ ├── manage-deck/ # Upload/details/access/actions sections
│ │ ├── AnalyticsChart.tsx # Responsive flex-1 bar chart
│ │ ├── AnalyticsDashboard.tsx # Tabbed stats overview
│ │ ├── ContentStatsCard.tsx # Inline stats row
│ │ └── DecksTable.tsx # Desktop table + Mobile card-list
│ ├── layout/ # Shell components
│ │ ├── DashboardLayout.tsx # Root layout: sidebar/header/FAB/BottomNav
│ │ ├── Sidebar.tsx # Desktop sidebar (md+)
│ │ └── BottomNav.tsx # Mobile bottom nav (< md)
│ ├── AccessGate # Interceptor for password/email gating
│ └── AnalyticsModal # Real-time charts and drop-off analytics
├── contexts/
│ └── AuthContext # Session watchdog and Pro-tier gating
├── pages/
│ └── DeckAnalytics # Per-deck engagement deep-dive
├── services/
│ ├── deckService # Composed facade over deck-focused modules
│ ├── deckStorageService
│ ├── deckLibraryService
│ ├── deckBrandingService
│ ├── authSession.ts # Shared auth/user-id resolution helpers
│ ├── analyticsService # Heartbeat tracking and analytics queries
│ ├── dataRoomService # Room management & document ordering
│ ├── interestSignalService # Visitor engagement & Room-level signal aggregation
│ ├── userService # Identity and user handle management
│ └── url.ts # Centralized share-link & routing logic
├── workflows/
│ └── deckProcessing.ts # Shared PDF rendering/image generation workflow
└── pages/
├── ManageDeck # Composition layer for upload/edit flow
├── DataRoomDetail # Internal room view with aggregated signals
└── Viewer # High-fidelity viewer with heartbeat tracking2. Database Schema (PostgreSQL + RLS)
Deckly uses Row Level Security (RLS) to ensure data isolation.
decks: Registry of documents, status, and slide URLs.profiles: User metadata and tier levels (FREE/PRO).deck_stats: Aggregate table for rapid dashboard rendering.deck_page_views: Registry for unique view deduplication (24h window).library_folders: Localized "Collections" for asset organization, including acoloridentifier (Default:#666666).library_tags: User-defined labels for multi-dimensional asset filtering.
System Deep-Dives
1. The PDF processing Warehouse
Processing happens entirely in the browser to eliminate server costs and maximize privacy.
sequenceDiagram
participant User
participant ManageDeck
participant Workflow
participant PDFjs
participant Storage
participant DB
User->>ManageDeck: Uploads PDF
ManageDeck->>Workflow: submitDeck / processPdfFile
Workflow->>PDFjs: Loads ArrayBuffer
loop Every Page
PDFjs->>Workflow: Render page to canvas
Workflow->>Workflow: Convert to WebP and collect links
end
Workflow->>Storage: Parallel Upload (Concurrency: 3)
Storage-->>Workflow: Return URLs
Workflow->>DB: Upsert Deck Pages & Status
DB-->>User: Navigate to Dashboard2. The Auth Watchdog Sequence
Prevents app hangs caused by Supabase free-tier "cold starts."
flowchart TD
A[App Initialization] --> B{auth.getSession}
B -->|Success < 10s| C[Profile Sync]
B -->|Timeout > 10s| D[Show 'Waking up DB' UI]
C --> E[Enable Dashboard]
D --> F[Wait 2s more]
F --> G{Still Loading?}
G -->|Yes| H[Force Clear Loader]
G -->|No| E3. Analytics Heartbeat & Deduplication
- Heartbeat: On slide change or tab close,
Viewer.tsxcallsanalyticsService.syncSlideStats()to perform an Atomic Upsert (total_views + 1). - Uniqueness: Uses a
visitorIdinlocalStorage. A view is only "unique" if the ID hasn't touched the slide in 24 hours. - Aggregation:
getRoomVisitorSignalsaggregates engagement data from all decks in a room to provide a unified "Room Interest" score.
4. Consistent Link Generation
To prevent broken links during routing schema updates, we centralize all public and internal URL generation in src/utils/url.ts. All copy-link actions and internal navigation must use these helpers to ensure a single source of truth for handle-based paths.
5. Current refactor direction
ManageDeck.tsxnow delegates orchestration touseManageDeckWorkflow.ts- the screen UI is split into
ManageDeckSections.tsx - shared PDF logic lives in
src/workflows/deckProcessing.ts - auth/session lookups are being standardized via
authSession.ts deckService.tsis now a facade over narrower services instead of one large implementation file
6. Bundle Optimization (Performance)
To maintain a premium feel, we use route-level code splitting via React.lazy and Suspense. Vendor libraries are isolated into a separate bundle via Vite's manualChunks to ensure minimal initial payload and effective caching of stable dependencies.
Developer Workflows
1. Setup & Standards
npm install&&cp .env.example .env.local- Always use Tailwind utility classes for layout and spacing, and CSS Variables from
App.css(e.g.,--accent-primary) for brand colours. Avoid ad-hoc inline styles. - Prefer Custom Hooks (
useAuth,useDecks) over direct Supabase client calls. - Run
npm run type-check,npm run lint, andnpm testbefore closing substantial work. The Vitest pipeline is healthy again.
2. The "New Feature" Recipe
- Type: Define interfaces in
src/types/index.ts. - Service: Create logic in
src/services/. - Component: Build in
src/components/using design tokens. - Analytics: Register temporary events in
analyticsService.ts.
3. Mobile-First UI Rule
All dashboard pages use a single md breakpoint (768px) to split mobile and desktop layouts. Key conventions every dev must follow:
- Navigation:
BottomNavon mobile,Sidebaron desktop. Never show both simultaneously. - Content padding: Always
p-4 md:p-8— never hard-code large padding without a mobile fallback. - Bottom clearance: Add
pb-20 md:pb-0to any page's scroll container so content doesn't hide behind the bottom nav bar (80px tall). - Tables on mobile: Use the dual-render pattern —
md:hiddencard-list above +hidden md:blocktable below. Reference:DecksTable.tsx. - Grids:
grid-cols-2 md:grid-cols-4for summary cards. - Fonts:
text-2xl md:text-5xlfor large stats. Always include a smaller mobile size. - Charts: Custom charts use
flex-1 max-w-12per bar so they naturally fit any container width without overflow.
Troubleshooting & FAQ
| Problem | Solution |
|---|---|
| Upload stuck at 0% | Ensure the decks bucket is Public and RLS allows INSERT. |
| Icons are missing | Check if the path in markdown matches /icons/[name].svg. |
| Stats not updating | Wait 30s for the memory cache to expire or use the manual refresh. |
| Auth hangs | Check VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY (ensure VITE_ prefix). |
| Content behind nav | Add pb-20 md:pb-0 to the page's scroll container. The bottom nav is 80px tall. |
| Chart bars too wide | Chart uses flex-1 max-w-12 — if you increase the number of data points beyond 14, reduce max-w-12 to max-w-8. |
This guide is the living constitution of the Deckly codebase. Last Updated: April 2026.
