# Screenshot Automation Plan for Bookhoard Documentation

> **Status**: Ready to implement when frontend is complete  
> **Last Updated**: 2026-02-03

This document outlines the complete plan for automatically generating screenshots for Bookhoard documentation using Playwright.

## Overview

Playwright will be used to:

1. Navigate the running Bookhoard server
2. Perform key user/admin workflows
3. Capture screenshots at each step
4. Save to `docs/images/` organized by documentation section
5. Generate/update markdown files with proper image references

## Prerequisites (To Verify When Ready)

### Frontend Pages Complete

Verify these pages are fully functional before starting:

- [ ] `/` - Login page
- [ ] `/register` - Registration page
- [ ] `/dashboard` - Main dashboard with library browsing
- [ ] `/collections` - Collections management
- [ ] `/devices` - Device management and registration
- [ ] `/conflicts` - Sync conflict resolution
- [ ] `/progress` - Reading progress tracking
- [ ] `/analytics` - Usage analytics
- [ ] `/queue` - Sync queue monitoring
- [ ] `/admin` - Admin dashboard
- [ ] `/admin/profile` - Profile settings
- [ ] `/admin/library` - Library management
- [ ] `/api-explorer` - API testing interface

### Test Environment Ready

- [ ] Bookhoard server running on `http://localhost:8765`
- [ ] Test database seeded with sample data (books, collections, devices)
- [ ] Test admin account ready (username, password, role=admin)
- [ ] Test regular user account ready (username, password, role=user)

## Installation & Setup

```bash
# Install Playwright (run in project root)
npm init -y
npm install -D @playwright/test
npx playwright install chromium
```

Project structure after setup:

```
bookhoard/
├── docs/
│   ├── images/           # Screenshots will be stored here
│   │   ├── user/
│   │   ├── admin/
│   │   ├── devices/
│   │   └── sync/
│   └── *.md              # Existing markdown files
├── screenshots/          # Playwright test files
│   ├── auth.spec.ts
│   ├── user-workflows.spec.ts
│   ├── admin-workflows.spec.ts
│   ├── device-workflows.spec.ts
│   ├── sync-workflows.spec.ts
│   └── config.ts
├── .env.screenshots      # Environment configuration
└── ...
```

## Environment Setup

Create `.env.screenshots` in the project root:

```env
# Bookhoard Server
BASE_URL=http://localhost:8765

# Admin Account (for admin guide screenshots)
ADMIN_EMAIL=admin@example.com
ADMIN_USERNAME=admin
ADMIN_PASSWORD=SecureAdminPass123!

# Regular User Account (for user guide screenshots)
USER_EMAIL=user@example.com
USER_USERNAME=user
USER_PASSWORD=SecureUserPass456!
```

## Questions to Ask When Ready

Before running the screenshot automation, ask the user:

### 1. Server Access

**Q**: Where is the Bookhoard server running?

- [ ] `localhost:8765` (default)
- [ ] Custom port: `________`
- [ ] Remote URL: `________`

### 2. Test Credentials

**Q**: What credentials should Playwright use?

**Admin Account** (for admin guide screenshots):

- Username: `________`
- Password: `________`

**Regular User Account** (for user guide screenshots):

- Username: `________`
- Password: `________`

### 3. Screenshot Format

**Q**: What format for screenshots?

- [ ] **WebP** (recommended - modern, good compression)
- [ ] PNG (highest quality, larger files)
- [ ] JPEG (smaller files, compression artifacts)

### 4. Screenshot Dimensions

**Q**: What viewport sizes for screenshots?

- [ ] **Desktop**: 1920x1080 (full-width screenshots)
- [ ] **Tablet**: 768x1024 (responsive documentation)
- [ ] **Mobile**: 375x667 (mobile documentation)
- [ ] All three sizes (comprehensive coverage)

### 5. Theme

**Q**: What theme should screenshots use?

- [ ] **Default** (Tokyo Night theme as seen in templates)
- [ ] Light theme (if implemented)
- [ ] Multiple themes (document theme switching)

### 6. Language

**Q**: What language/region for the UI?

- [ ] **English** (default)
- [ ] Other: `________`

## Screenshot Plan by Documentation Section

### 1. User Guide Screenshots

**File**: `docs/user/user-guide.md`

**Screenshots Needed**:

| Screenshot Name           | Description                        | Page/Action                             |
| ------------------------- | ---------------------------------- | --------------------------------------- |
| `login-page.webp`         | Login form with filled credentials | `/` - Login page                        |
| `dashboard-overview.webp` | Main dashboard showing libraries   | `/dashboard`                            |
| `library-grid.webp`       | Media items grid view              | `/dashboard` → Click library            |
| `book-detail.webp`        | Book detail view with metadata     | `/dashboard` → Click book               |
| `search-results.webp`     | Search in action                   | `/dashboard` → Type in search           |
| `filter-panel.webp`       | Filter options expanded            | `/dashboard` → Open filters             |
| `collections-list.webp`   | Collections grid view              | `/collections`                          |
| `create-collection.webp`  | New collection modal               | `/collections` → Click "New Collection" |
| `reading-progress.webp`   | Progress tracking view             | `/progress`                             |
| `analytics-view.webp`     | User analytics dashboard           | `/analytics`                            |

**Estimated Screenshots**: ~10

### 2. Admin Guide Screenshots

**File**: `docs/user/admin-guide.md`

**Screenshots Needed**:

| Screenshot Name          | Description               | Page/Action                            |
| ------------------------ | ------------------------- | -------------------------------------- |
| `admin-dashboard.webp`   | Admin overview panel      | `/admin`                               |
| `user-management.webp`   | User list with actions    | `/admin` → Users section               |
| `add-user.webp`          | Add new user form         | `/admin` → Click "Add User"            |
| `library-settings.webp`  | Library configuration     | `/admin/library`                       |
| `add-library.webp`       | Create new library form   | `/admin/library` → Click "Add Library" |
| `analytics-admin.webp`   | Admin analytics view      | `/analytics` (admin view)              |
| `sync-queue.webp`        | Sync queue monitoring     | `/queue`                               |
| `theme-settings.webp`    | Theme selection interface | `/admin/profile` → Theme section       |
| `user-profile-edit.webp` | Edit user profile         | `/admin/profile`                       |

**Estimated Screenshots**: ~9

### 3. Device Setup Screenshots

**File**: `docs/user/devices/kobo-setup.md` and `koreader-setup.md`

**Screenshots Needed**:

| Screenshot Name             | Description                       | Page/Action                         |
| --------------------------- | --------------------------------- | ----------------------------------- |
| `device-list.webp`          | Device management page            | `/devices`                          |
| `add-device-modal.webp`     | Add new device modal              | `/devices` → Click "Add New Device" |
| `device-form-kobo.webp`     | Kobo device registration form     | `/devices` → Select Kobo type       |
| `device-form-koreader.webp` | KOReader device registration form | `/devices` → Select KOReader type   |
| `device-qr-code.webp`       | QR code for device approval       | After device registration           |
| `device-approved.webp`      | Device approved confirmation      | After approving device              |
| `device-sync-settings.webp` | Sync configuration for device     | `/devices` → Click device settings  |
| `shelf-mapping.webp`        | Collection to shelf mapping       | `/devices` → Click shelf mapping    |
| `sync-queue-item.webp`      | Device sync in queue              | `/queue` (device specific)          |

**Estimated Screenshots**: ~9

### 4. Sync Guide Screenshots

**File**: `docs/user/sync-guide.md`

**Screenshots Needed**:

| Screenshot Name            | Description               | Page/Action                    |
| -------------------------- | ------------------------- | ------------------------------ |
| `sync-conflicts.webp`      | Conflicts list view       | `/conflicts`                   |
| `conflict-resolution.webp` | Resolve conflict dialog   | `/conflicts` → Click resolve   |
| `unlinked-books.webp`      | Unlinked books list       | `/unlinked-books`              |
| `book-linking.webp`        | Link book to metadata     | `/unlinked-books` → Click link |
| `sync-success.webp`        | Successful sync indicator | Any page after sync            |

**Estimated Screenshots**: ~5

### 5. Settings Guide Screenshots

**File**: `docs/user/settings-guide.md`

**Screenshots Needed**:

| Screenshot Name         | Description              | Page/Action                       |
| ----------------------- | ------------------------ | --------------------------------- |
| `profile-overview.webp` | Profile settings page    | `/admin/profile`                  |
| `update-username.webp`  | Username change form     | `/admin/profile`                  |
| `update-email.webp`     | Email change form        | `/admin/profile`                  |
| `change-password.webp`  | Password change form     | `/admin/profile`                  |
| `theme-selector.webp`   | Theme selection dropdown | `/admin/profile` (if implemented) |

**Estimated Screenshots**: ~5

## Playwright Test Files

### `screenshots/config.ts` - Playwright Configuration

```typescript
import { defineConfig, devices } from "@playwright/test";

export default defineConfig({
  testDir: "./",
  fullyParallel: false,
  retries: 1,
  reporter: "list",
  use: {
    baseURL: process.env.BASE_URL || "http://localhost:8765",
    trace: "on-first-retry",
    screenshot: "only-on-failure",
  },
  projects: [
    {
      name: "chromium-desktop",
      use: {
        ...devices["Desktop Chrome"],
        viewport: { width: 1920, height: 1080 },
      },
    },
  ],
});
```

### `screenshots/auth.spec.ts` - Authentication Screenshots

```typescript
import { test, expect } from "@playwright/test";

test.describe("Authentication Screenshots", () => {
  test("Login page", async ({ page }) => {
    await page.goto("/");

    await page.screenshot({
      path: "docs/images/user/login-page.webp",
      fullPage: true,
    });
  });

  test("User login flow", async ({ page }) => {
    await page.goto("/");

    await page.fill('input[name="login"]', process.env.USER_USERNAME || "user");
    await page.fill(
      'input[name="password"]',
      process.env.USER_PASSWORD || "password",
    );

    await page.screenshot({
      path: "docs/images/user/login-form-filled.webp",
      fullPage: true,
    });

    await page.click('button[type="submit"]');
    await page.waitForURL("/dashboard", { timeout: 5000 });

    await page.screenshot({
      path: "docs/images/user/dashboard-after-login.webp",
      fullPage: true,
    });
  });

  test("Admin login flow", async ({ page }) => {
    await page.goto("/");

    await page.fill(
      'input[name="login"]',
      process.env.ADMIN_USERNAME || "admin",
    );
    await page.fill(
      'input[name="password"]',
      process.env.ADMIN_PASSWORD || "password",
    );

    await page.click('button[type="submit"]');
    await page.waitForURL("/dashboard", { timeout: 5000 });

    await page.goto("/admin");

    await page.screenshot({
      path: "docs/images/admin/admin-dashboard.webp",
      fullPage: true,
    });
  });

  test("Registration page", async ({ page }) => {
    await page.goto("/register");

    await page.screenshot({
      path: "docs/images/user/registration-page.webp",
      fullPage: true,
    });

    await page.fill('input[name="email"]', "newuser@example.com");
    await page.fill('input[name="username"]', "newuser");
    await page.fill('input[name="password"]', "SecurePass123!");

    await page.screenshot({
      path: "docs/images/user/registration-form-filled.webp",
      fullPage: true,
    });
  });
});
```

### `screenshots/user-workflows.spec.ts` - User Guide Screenshots

```typescript
import { test, expect } from "@playwright/test";

test.describe("User Guide Screenshots", () => {
  test.beforeEach(async ({ page }) => {
    await page.goto("/");
    await page.fill('input[name="login"]', process.env.USER_USERNAME || "user");
    await page.fill(
      'input[name="password"]',
      process.env.USER_PASSWORD || "password",
    );
    await page.click('button[type="submit"]');
    await page.waitForURL("/dashboard", { timeout: 5000 });
  });

  test("Dashboard overview", async ({ page }) => {
    await page.goto("/dashboard");

    await page.screenshot({
      path: "docs/images/user/dashboard-overview.webp",
      fullPage: true,
    });
  });

  test("Library grid view", async ({ page }) => {
    await page.goto("/dashboard");

    await page.waitForSelector("#libraries-container", { timeout: 5000 });

    const firstLibrary = page.locator("[data-library]").first();
    if (await firstLibrary.isVisible()) {
      await firstLibrary.click();
      await page.waitForURL(/\/dashboard/, { timeout: 5000 });

      await page.screenshot({
        path: "docs/images/user/library-grid.webp",
        fullPage: true,
      });
    }
  });

  test("Search functionality", async ({ page }) => {
    await page.goto("/dashboard");

    await page.waitForSelector("#search-input", { timeout: 5000 });

    await page.fill("#search-input", "science");
    await page.waitForTimeout(1000);

    await page.screenshot({
      path: "docs/images/user/search-results.webp",
      fullPage: true,
    });
  });

  test("Filter panel", async ({ page }) => {
    await page.goto("/dashboard");

    const filterPanel = page.locator(".filter-panel details");
    if (await filterPanel.isVisible()) {
      await filterPanel.click();
      await page.waitForTimeout(500);

      await page.screenshot({
        path: "docs/images/user/filter-panel-open.webp",
        fullPage: true,
      });
    }
  });

  test("Collections list", async ({ page }) => {
    await page.goto("/collections");

    await page.screenshot({
      path: "docs/images/user/collections-list.webp",
      fullPage: true,
    });
  });

  test("Create collection modal", async ({ page }) => {
    await page.goto("/collections");

    await page.click(
      'button:has-text("New Collection"), button:has-text("Create Your First Collection")',
    );

    await page.waitForSelector("#create-modal", {
      state: "visible",
      timeout: 5000,
    });

    await page.fill("#collection-name", "My Reading List");
    await page.fill("#collection-description", "Books I want to read");

    await page.screenshot({
      path: "docs/images/user/create-collection-modal.webp",
      fullPage: true,
    });
  });

  test("Reading progress view", async ({ page }) => {
    await page.goto("/progress");

    await page.screenshot({
      path: "docs/images/user/reading-progress.webp",
      fullPage: true,
    });
  });

  test("Analytics view", async ({ page }) => {
    await page.goto("/analytics");

    await page.screenshot({
      path: "docs/images/user/analytics-view.webp",
      fullPage: true,
    });
  });
});
```

### `screenshots/admin-workflows.spec.ts` - Admin Guide Screenshots

```typescript
import { test, expect } from "@playwright/test";

test.describe("Admin Guide Screenshots", () => {
  test.beforeEach(async ({ page }) => {
    await page.goto("/");
    await page.fill(
      'input[name="login"]',
      process.env.ADMIN_USERNAME || "admin",
    );
    await page.fill(
      'input[name="password"]',
      process.env.ADMIN_PASSWORD || "password",
    );
    await page.click('button[type="submit"]');
    await page.waitForURL("/dashboard", { timeout: 5000 });
  });

  test("Admin dashboard", async ({ page }) => {
    await page.goto("/admin");

    await page.screenshot({
      path: "docs/images/admin/admin-dashboard.webp",
      fullPage: true,
    });
  });

  test("User management", async ({ page }) => {
    await page.goto("/admin");

    const userSection = page.locator('[data-section="users"], text="Users"');
    if (await userSection.isVisible()) {
      await page.screenshot({
        path: "docs/images/admin/user-management.webp",
        fullPage: true,
      });
    }
  });

  test("Library management", async ({ page }) => {
    await page.goto("/admin/library");

    await page.screenshot({
      path: "docs/images/admin/library-management.webp",
      fullPage: true,
    });
  });

  test("Add library form", async ({ page }) => {
    await page.goto("/admin/library");

    const addLibraryBtn = page.locator(
      'button:has-text("Add Library"), button:has-text("Create Library")',
    );
    if (await addLibraryBtn.isVisible()) {
      await addLibraryBtn.click();
      await page.waitForTimeout(500);

      await page.screenshot({
        path: "docs/images/admin/add-library-form.webp",
        fullPage: true,
      });
    }
  });

  test("Profile settings", async ({ page }) => {
    await page.goto("/admin/profile");

    await page.screenshot({
      path: "docs/images/admin/profile-settings.webp",
      fullPage: true,
    });
  });

  test("Theme selector", async ({ page }) => {
    await page.goto("/admin/profile");

    const themeSelect = page.locator(
      'select[name="theme"], [data-theme-selector]',
    );
    if (await themeSelect.isVisible()) {
      await themeSelect.click();
      await page.waitForTimeout(500);

      await page.screenshot({
        path: "docs/images/admin/theme-selector.webp",
        fullPage: true,
      });
    }
  });
});
```

### `screenshots/device-workflows.spec.ts` - Device Setup Screenshots

```typescript
import { test, expect } from "@playwright/test";

test.describe("Device Setup Screenshots", () => {
  test.beforeEach(async ({ page }) => {
    await page.goto("/");
    await page.fill('input[name="login"]', process.env.USER_USERNAME || "user");
    await page.fill(
      'input[name="password"]',
      process.env.USER_PASSWORD || "password",
    );
    await page.click('button[type="submit"]');
    await page.waitForURL("/dashboard", { timeout: 5000 });
  });

  test("Device list page", async ({ page }) => {
    await page.goto("/devices");

    await page.screenshot({
      path: "docs/images/devices/device-list.webp",
      fullPage: true,
    });
  });

  test("Add device modal", async ({ page }) => {
    await page.goto("/devices");

    await page.click(
      'button:has-text("Add New Device"), button:has-text("Add Your First Device")',
    );

    await page.waitForSelector('[data-modal="add-device"], #add-device-modal', {
      state: "visible",
      timeout: 5000,
    });

    await page.screenshot({
      path: "docs/images/devices/add-device-modal.webp",
      fullPage: true,
    });
  });

  test("Kobo device form", async ({ page }) => {
    await page.goto("/devices");

    await page.click('button:has-text("Add New Device")');

    await page.waitForSelector('[data-modal="add-device"]', {
      state: "visible",
      timeout: 5000,
    });

    const deviceTypeSelect = page.locator('select[name="device_type"]');
    if (await deviceTypeSelect.isVisible()) {
      await deviceTypeSelect.selectOption("kobo");
      await page.waitForTimeout(500);

      await page.screenshot({
        path: "docs/images/devices/device-form-kobo.webp",
        fullPage: true,
      });
    }
  });

  test("KOReader device form", async ({ page }) => {
    await page.goto("/devices");

    await page.click('button:has-text("Add New Device")');

    await page.waitForSelector('[data-modal="add-device"]', {
      state: "visible",
      timeout: 5000,
    });

    const deviceTypeSelect = page.locator('select[name="device_type"]');
    if (await deviceTypeSelect.isVisible()) {
      await deviceTypeSelect.selectOption("koreader");
      await page.waitForTimeout(500);

      await page.screenshot({
        path: "docs/images/devices/device-form-koreader.webp",
        fullPage: true,
      });
    }
  });

  test("Sync queue", async ({ page }) => {
    await page.goto("/queue");

    await page.screenshot({
      path: "docs/images/devices/sync-queue.webp",
      fullPage: true,
    });
  });
});
```

### `screenshots/sync-workflows.spec.ts` - Sync Guide Screenshots

```typescript
import { test, expect } from "@playwright/test";

test.describe("Sync Guide Screenshots", () => {
  test.beforeEach(async ({ page }) => {
    await page.goto("/");
    await page.fill('input[name="login"]', process.env.USER_USERNAME || "user");
    await page.fill(
      'input[name="password"]',
      process.env.USER_PASSWORD || "password",
    );
    await page.click('button[type="submit"]');
    await page.waitForURL("/dashboard", { timeout: 5000 });
  });

  test("Sync conflicts page", async ({ page }) => {
    await page.goto("/conflicts");

    await page.screenshot({
      path: "docs/images/sync/sync-conflicts.webp",
      fullPage: true,
    });
  });

  test("Unlinked books page", async ({ page }) => {
    await page.goto("/unlinked-books");

    await page.screenshot({
      path: "docs/images/sync/unlinked-books.webp",
      fullPage: true,
    });
  });
});
```

## Running the Tests

### Run all tests

```bash
npx playwright test
```

### Run specific test file

```bash
npx playwright test auth.spec.ts
```

### Run in headed mode (see browser)

```bash
npx playwright test --headed
```

### Run with debug mode

```bash
npx playwright test --debug
```

## Markdown Update Strategy

### Option 1: Create New Markdown

Generate fresh markdown files with embedded screenshots.

### Option 2: Update Existing Markdown

Update existing markdown files by:

1. Finding section headers
2. Inserting screenshot references after relevant steps
3. Using alt text to describe what's shown

**Example Markdown Update**:

```markdown
### Step 1: Log In to Bookhoard

1. Open your browser and navigate to `http://localhost:8765`
2. Enter your username and password
3. Click the **Login** button

![Login page showing username and password fields](../images/user/login-page.webp)
```

## Workflow When Ready

### Step 1: Verify Frontend Complete

- Check all pages listed in "Prerequisites" are working
- Confirm no TODO placeholders in templates

### Step 2: Seed Test Data

```bash
# Add sample books, collections, devices
# Create test admin and user accounts
```

### Step 3: Set Up Playwright

```bash
npm install -D @playwright/test
npx playwright install chromium
```

### Step 4: Create Environment File

Create `.env.screenshots` with the test credentials

### Step 5: Run Screenshot Scripts

```bash
# Run all screenshot workflows
npx playwright test

# Or run specific suites
npx playwright test auth.spec.ts
npx playwright test user-workflows.spec.ts
```

### Step 6: Review and Adjust

- Manually review screenshots
- Retake any that need adjustment
- Update markdown files if needed

## Customizing for Your Implementation

When you're ready to run these, you may need to update:

1. **Selectors**: Update CSS selectors to match your actual HTML structure
2. **Routes**: Ensure all routes match your router configuration
3. **Wait conditions**: Adjust timeouts based on your app's performance
4. **Modal IDs**: Update modal selectors to match your implementation

## Tips for Better Screenshots

1. **Wait for animations**: Add `page.waitForTimeout(500)` after clicking to let animations complete
2. **Full page vs viewport**: Use `fullPage: true` for complete page screenshots
3. **Hide scrollbars**: Use `page.evaluate(() => document.body.style.overflow = 'hidden')` for cleaner screenshots
4. **Dark theme**: Most screenshots will use the default Tokyo Night theme
5. **Clean test data**: Use consistent test data for reproducible screenshots

## Estimated Time Investment

| Task                           | Time          |
| ------------------------------ | ------------- |
| Install & configure Playwright | 15 min        |
| Seed test database             | 30 min        |
| Write Playwright scripts       | 2-3 hours     |
| Run screenshot automation      | 10 min        |
| Review & retake screenshots    | 30-60 min     |
| Update markdown files          | 30-60 min     |
| **Total**                      | **4-6 hours** |

## Future Enhancements

- **Visual Regression Testing**: Use Playwright to detect UI changes
- **Multi-theme Screenshots**: Automatically capture all theme variations
- **Multi-language Screenshots**: Capture screenshots in different locales
- **Auto-update on Deploy**: Run screenshots as part of CI/CD on documentation updates

## References

- [Playwright Documentation](https://playwright.dev/)
- [Playwright Screenshots API](https://playwright.dev/docs/screenshots)
- [Bookhoard Frontend Templates](templates/)
- [Bookhoard API Documentation](docs/developer/api/)

---

**Created by**: GLM (OpenCode)  
**Version**: 1.0  
**Ready for Implementation**: When frontend development is complete
