# Vendor Tasks for Dokan & ClickUp — Brand & Asset System

Everything in `.wordpress-org/` is generated. `icon.svg` and
`resources/brand/banner.html` are the only files edited by hand — never touch
the PNGs directly, they are overwritten on every render.

---

## 1. Asset Inventory

| File | Dimensions | Purpose | Source |
|---|---|---|---|
| `icon.svg` | vector | Canonical icon. WordPress.org consumes this directly when present. | Authored here (master) |
| `icon-256x256.png` | 256×256 | Directory hero icon (retina) | Rasterised from `icon.svg` |
| `icon-128x128.png` | 128×128 | Directory listing grid | Rasterised from `icon.svg` |
| `icon-512x512.png` | 512×512 | Non-directory channels that ask for a larger mark | Rasterised from `icon.svg` |
| `banner-1544x500.png` | 1544×500 | Desktop directory banner (retina) | `resources/brand/banner.html` |
| `banner-772x250.png` | 772×250 | Mobile / non-retina banner | Same markup, narrow variant |
| `banner-1024x512.png` | 1024×512 | Square-ish crop for channels outside the directory | Same markup, square variant |
| `screenshot-*.png` | 1200×900 | Feature screenshots, branded frame | Live site capture, composed |

Only the 128/256 icons and the 772×250 / 1544×500 banners are used by the
plugin directory itself; the other sizes exist for other channels and cost
nothing to keep in step.

### What this replaced

Two hand-authored banner SVGs, one per size, each carrying its own hex list —
and a flat icon whose ground was `#5b2ee5`, a violet that appeared nowhere in
either banner. The mark sat on a colour the icon never used. There is now one
palette, and the icon and banners are visibly cut from the same material.

The screenshots had a subtler problem: they were real captures of a plugin
nobody had set up. "Not connected yet", every counter an em dash or a zero,
"No activity yet", a setup checklist reading 0/4 — a listing advertising
software doing nothing. `resources/dev/seed.php` exists so the same screens
photograph a marketplace mid-flow.

There were also nine screenshot PNGs and seven captions. WordPress.org pairs
them by number, so the last two were published uncaptioned; they are gone.

---

## 2. Design Tokens

### 2.1 Colour

**The palette lives in `tests/assets/brand.ts`, and only there.** The icon, the
banners and the screenshot frames all import it. Do not copy a hex out of that
file into another one — that is precisely how the icon and the banners came to
disagree.

`icon.svg` is the exception the format forces: SVG cannot import. Its values are
duplicated deliberately and its header comment names `brand.ts` as canonical.

Every token is a Tailwind **violet** stop. The hue is inherited — the previous
artwork was violet and is live on the directory, so changing it would spend what
recognition the listing has. What changed is that the values are the scale
itself rather than one-off mixes of it.

| Token | Hex | Tailwind | Used for |
|---|---|---|---|
| `ink` | `#2e1065` | violet-950 | Ground, darkest corner |
| `inkMid` | `#4c1d95` | violet-900 | Ground, middle |
| `inkLift` | `#5b21b6` | violet-800 | Ground, lit corner |
| `royal` | `#7c3aed` | violet-600 | Glow, primary |
| `royalLight` | `#8b5cf6` | violet-500 | Reserved |
| `sky` | `#a78bfa` | violet-400 | Frame kicker |
| `accent` | `#c4b5fd` | violet-300 | Wordmark gradient, text on the ground |
| `faceCrown` / `faceMid` / `faceBase` | `#ffffff` / `#f5f3ff` / `#ddd6fe` | white / violet-50 / violet-200 | The mark, crown to base |
| `shadow` | `46, 16, 101` | violet-950 | Shadow colour, as RGB channels |

White on `violet-800` is 8.1:1 and `violet-300` on `violet-950` is 9.4:1 — both
clear WCAG AA with room, which matters for a palette whose brightest accent is
also the wordmark.

### 2.2 The mark

A circular arrow closing on a tick: something happened in the marketplace, and
something was done about it without anybody being asked. That is the whole
plugin, and both halves are idioms the audience already reads.

Unchanged in direction from the icon it replaces. It is inset further, because
at 128px — the size the directory grid actually uses — the arrowhead was running
into the squircle's radius.

---

## 3. Regenerating

```bash
yarn shots:banners   # icon + banners — renders local markup, no site needed

wp eval-file resources/dev/seed.php                      # the demo marketplace

# Cookies minted by the site itself — no password, no login plugin.
WP_AUTH_COOKIES="$(wp eval 'echo wp_json_encode([
  ["name" => LOGGED_IN_COOKIE, "value" => wp_generate_auth_cookie( 1, time() + 7200, "logged_in" ), "path" => SITECOOKIEPATH],
  ["name" => AUTH_COOKIE,      "value" => wp_generate_auth_cookie( 1, time() + 7200, "auth" ),      "path" => ADMIN_COOKIE_PATH],
]);')" yarn shots:wporg
```

`WP_BASE_URL` overrides the site. `WP_LOGIN_URL` (a magic link) and
`WP_ADMIN_USER` / `WP_ADMIN_PASS` also work — the cookie route is first here
because it needs neither a password nor a healthy login plugin, and the site
these were captured against had a magic-login handler that rejected its own
freshly issued tokens as "expired or already used".

Two things the capture site needs beyond that: **Freemius opt-in skipped**, or
its full-page prompt covers the admin instead of the plugin —

```bash
wp eval 'vendor_tasks_dokan_clickup_freemius()->skip_connection();'
```

— and a **built bundle**, since the admin is a React app: run `yarn build`
first, or the screens photograph as blank.

The capture site needs **Dokan active** — this plugin refuses to activate
without it — and it must not be a production environment. Check before seeding,
every time, because a `.test` domain is not evidence of anything:

```bash
wp eval 'echo wp_get_environment_type();'
```

Adding a screenshot means adding a numbered entry to `readme.txt` under
`== Screenshots ==` as well. WordPress.org matches them by number, not by name.

### The demo data

`resources/dev/seed.php` puts the plugin into the state it reaches once it is
working: eight vendors carrying ClickUp task ids, a connected settings payload,
ten triggers on, and an eight-row activity feed spread across two days.

```bash
wp eval-file resources/dev/seed.php list                  # the scopes
wp eval-file resources/dev/seed.php only=vendors          # one of them
wp eval-file resources/dev/seed.php remove                # all of it, backwards
```

The API token it writes is not a token — it is a string of the right shape, so
the connection UI shows its masked connected state. Every call that token would
be used for is against ClickUp, and none of them happen during a capture.

Settings are backed up before they are overwritten and restored on `remove`,
so running this against a site that had real settings does not cost them.

The arguments carry no dashes on purpose. WP-CLI parses dash-prefixed words as
its own parameters before `eval-file` hands them on, so `-- --remove` fails with
"unknown --remove parameter" and nothing runs at all — which looks exactly like
a successful removal if you are not reading the output.

One row in the activity feed is a failure, deliberately. A feed of nothing but
successes describes software that has never met a network.

---

## 4. Capturing honestly

The frame around each screenshot is branded; what is inside it is not touched.

- **Nothing is mocked.** Every pixel inside the card came from the plugin
  running against a real database.
- **Seeded, not faked.** The difference matters: the numbers on the Overview are
  counted from real rows the seed created, by the plugin's own queries. Nothing
  writes a number into the UI.
- **Other plugins do not appear**, and neither do admin notices — a listing
  screenshot should not open with somebody's update nag.
- **Each shot waits for its own heading** before capturing, so a screen that
  failed to mount fails the run instead of being photographed blank.
