Skip to content

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

LayerTechnologyPurpose
FrameworkReact 19 + ViteHigh-performance SPA with route-level code splitting.
LogicTypeScriptStrict type safety for complex data shapes.
Auth/DBSupabaseIdentity, PostgreSQL, and namespaced URL routing logic.
ProcessingPDF.js (Browser)Privacy-first, zero-cost client-side rendering.
AnalyticsPostHog + CustomCombined deep UX events with aggregated Room visitor signals.
StylingTailwind CSS + shadcn/uiUtility-first classes + accessible component primitives. Custom design tokens in App.css.

Project Architecture

1. File Structure

text
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 tracking

2. 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 a color identifier (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.

mermaid
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 Dashboard

2. The Auth Watchdog Sequence

Prevents app hangs caused by Supabase free-tier "cold starts."

mermaid
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| E

3. Analytics Heartbeat & Deduplication

  • Heartbeat: On slide change or tab close, Viewer.tsx calls analyticsService.syncSlideStats() to perform an Atomic Upsert (total_views + 1).
  • Uniqueness: Uses a visitorId in localStorage. A view is only "unique" if the ID hasn't touched the slide in 24 hours.
  • Aggregation: getRoomVisitorSignals aggregates engagement data from all decks in a room to provide a unified "Room Interest" score.

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.tsx now delegates orchestration to useManageDeckWorkflow.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.ts is 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

  1. npm install && cp .env.example .env.local
  2. 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.
  3. Prefer Custom Hooks (useAuth, useDecks) over direct Supabase client calls.
  4. Run npm run type-check, npm run lint, and npm test before closing substantial work. The Vitest pipeline is healthy again.

2. The "New Feature" Recipe

  1. Type: Define interfaces in src/types/index.ts.
  2. Service: Create logic in src/services/.
  3. Component: Build in src/components/ using design tokens.
  4. 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: BottomNav on mobile, Sidebar on 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-0 to any page's scroll container so content doesn't hide behind the bottom nav bar (80px tall).
  • Tables on mobile: Use the dual-render patternmd:hidden card-list above + hidden md:block table below. Reference: DecksTable.tsx.
  • Grids: grid-cols-2 md:grid-cols-4 for summary cards.
  • Fonts: text-2xl md:text-5xl for large stats. Always include a smaller mobile size.
  • Charts: Custom charts use flex-1 max-w-12 per bar so they naturally fit any container width without overflow.

Troubleshooting & FAQ

ProblemSolution
Upload stuck at 0%Ensure the decks bucket is Public and RLS allows INSERT.
Icons are missingCheck if the path in markdown matches /icons/[name].svg.
Stats not updatingWait 30s for the memory cache to expire or use the manual refresh.
Auth hangsCheck VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY (ensure VITE_ prefix).
Content behind navAdd pb-20 md:pb-0 to the page's scroll container. The bottom nav is 80px tall.
Chart bars too wideChart 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.

Built with ❤️ for Founders