# Adding a new article

No code changes. Create a folder, write the article, add one line to `myst.yml`'s `toc`,
commit, push.

## 1. Create the folder

```
articles/<category>/<slug>/
    <slug>.md          # or <slug>.ipynb
    article.yml        # publication metadata
    banner.png         # optional
    references.bib     # optional
    images/            # optional
```

Categories currently in use: `cartography`, `gis`, `geoai`, `analytics`. A new category is
just a new folder — add its display name to `CATEGORY_TITLES` in
`scripts/build_manifest.py` if you want a nicer heading.

## 2. Write the article frontmatter

```yaml
---
title: "Your Article Title"
subtitle: One-line subtitle
description: One or two sentences used for previews and social cards.
date: 2026-03-14
keywords:
  - ArcGIS Pro
  - Cartography
exports:
  - template: lapreprint-typst   # optional PDF export
---
```

Authors, license, and open-access flags are inherited from `myst.yml`. Override them per
article only when they differ.

## 3. Write `article.yml`

```yaml
slug: your-slug
category: cartography
featured: false
tags: [ArcGIS Pro, Cartography]
date: 2026-03-14          # fallback if frontmatter has no date
thumbnail: ./banner.png
banner: ./banner.png
series: null              # or a series id, e.g. topo-grids
series_part: null         # 1, 2, 3 ... within the series
series_title: null        # display name for the series
canonical_source: null    # original LinkedIn/blog URL, if any
```

Why a separate file: MyST validates its frontmatter keys and warns on unknown ones, so
publication-only fields (`category`, `featured`, `series`, ...) live here. MyST ignores
`article.yml`; the manifest script reads it.

### Standalone vs series

- **Standalone:** leave `series`, `series_part`, `series_title` as `null`.
- **Series:** give every part the same `series` id and `series_title`, and set
  `series_part` to 1, 2, 3 ... Parts are grouped and ordered automatically in the index
  and in `articles.json`. No implementation change is ever needed for a new series.

## 4. Register it in the table of contents

Add one line under the right category in `myst.yml`:

```yaml
- file: articles/cartography/your-slug/your-slug.md
```

This is the only file you touch outside your article folder. It exists so ordering is
yours rather than alphabetical.

## 5. Commit and push

GitHub Actions then:

1. runs `python scripts/build_manifest.py` — writes `public/articles.json` and
   `articles/_index-body.md`
2. runs `myst build --html`
3. copies `public/articles.json` into `_build/html/` (MyST does not pass `public/` through)
4. deploys `_build/html` to GitHub Pages

The article index, reading times, category counts, and series groupings all update on
their own.

## Reading time

Never typed by hand. The script strips frontmatter, code, math, directives, and image
captions, counts words, and divides by `WORDS_PER_MINUTE` (225) in
`scripts/build_manifest.py`. Change that constant to retune every article at once.

## The manifest

Published at `<site>/articles.json`. Shape:

```json
{
  "generated": "...", "site": "...", "count": 3,
  "categories": [{ "id": "cartography", "title": "Cartography", "count": 3 }],
  "series": [{ "id": "topo-grids", "title": "Topo Grids & Graticules",
               "parts": [{ "slug": "topo-grids-part-1", "part": 1, "title": "...", "url": "..." }] }],
  "articles": [{ "slug": "...", "category": "...", "title": "...", "description": "...",
                 "date": "...", "tags": [], "authors": [], "banner": "...", "thumbnail": "...",
                 "featured": true, "series": null, "series_part": null,
                 "word_count": 1980, "reading_time_minutes": 9,
                 "path": "...", "url": "...", "canonical_source": "..." }]
}
```

The React portfolio site will later `fetch()` this file — no duplicated content, no
second repository to update.

## Run it locally

```bash
pip install pyyaml
python scripts/build_manifest.py
myst start
```

The script exits non-zero with a clear message if an article is missing a title, date, or
source file, so a broken article fails the build instead of silently vanishing.
