Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
db90be2
creates ai_file_utils plugin and AI file actions data model.
dawnkelly09 Feb 4, 2026
4622482
fix prompting/content for open with LLM actions
dawnkelly09 Feb 4, 2026
d84d2b8
update docs, readme, toml
dawnkelly09 Feb 4, 2026
a7f0f5c
typo
dawnkelly09 Feb 4, 2026
762cadd
update test
dawnkelly09 Feb 4, 2026
5ac6ec3
adds copy markdown assertion to test
dawnkelly09 Feb 4, 2026
d14bb5a
updates per copilot review
dawnkelly09 Feb 5, 2026
15c9a04
covert plugin to a helper/util script
dawnkelly09 Feb 5, 2026
2199147
remove ai file plugin from list on readme
dawnkelly09 Feb 5, 2026
5989a45
updates per review feedback
dawnkelly09 Feb 5, 2026
7f906e5
ai-resources page plugin
dawnkelly09 Feb 6, 2026
52c84cf
adds note after table, docs
dawnkelly09 Feb 6, 2026
3a3913c
add back heading/copy
dawnkelly09 Feb 6, 2026
e7a5091
Update docs/ai-file-utils.md
dawnkelly09 Feb 6, 2026
cce2206
Merge pull request #7 from papermoonio/create-ai-file-utils
eshaben Feb 9, 2026
11d67e2
changes applied from Copilot review in GH
dawnkelly09 Feb 9, 2026
28ac90a
changes applied per feedback
dawnkelly09 Feb 9, 2026
4ab2b97
adds ai file actions plugin
dawnkelly09 Feb 11, 2026
e71e29e
updates to docs and readme
dawnkelly09 Feb 11, 2026
c8bf631
improved handling of category files to use category_id rather than di…
dawnkelly09 Feb 11, 2026
446d66f
updates per copilot review
dawnkelly09 Feb 11, 2026
fa8e6fa
improve ai-file-actions plugin
dawnkelly09 Feb 12, 2026
2177022
Merge branch 'staging-ai-resources-plugin' into build-ai-resources-pl…
dawnkelly09 Feb 16, 2026
7ffb188
Merge branch 'build-ai-resources-plugin' into adds-action-column-drop…
dawnkelly09 Feb 16, 2026
222d58b
refactor to improve design approach
dawnkelly09 Feb 16, 2026
227f456
update docs and readme to reflect code refactor
dawnkelly09 Feb 16, 2026
21871f4
updates related to removing categories_order
dawnkelly09 Feb 16, 2026
aa25d05
updates to remove unneeded JS dependence
dawnkelly09 Feb 16, 2026
f4dd217
adds per-page AI actions plugin
dawnkelly09 Feb 17, 2026
145147e
update plugin docs, improve separation of concerns for plugin
dawnkelly09 Feb 17, 2026
a273957
refactor to separate plugins from utils/lib, update docs, etc.
dawnkelly09 Feb 17, 2026
ffdbabc
Update docs/ai-resources-page.md
dawnkelly09 Feb 18, 2026
d9e17f1
fix lib naming issue. build working again.
dawnkelly09 Feb 18, 2026
95476e4
update docs to separate helpers from plugins
dawnkelly09 Feb 18, 2026
0f8deb9
fixes to jinja url creation
dawnkelly09 Feb 18, 2026
2d78c7c
fix open in llm prompt & url construction
dawnkelly09 Feb 18, 2026
df6b19f
Merge pull request #10 from papermoonio/adds-action-column-dropdown
eshaben Feb 18, 2026
1ceeb49
fix for "open in xyz" routing
dawnkelly09 Feb 18, 2026
6a1490d
address copilot feedback
dawnkelly09 Feb 18, 2026
179c8e9
copilot feedback
dawnkelly09 Feb 18, 2026
365803d
Merge pull request #8 from papermoonio/build-ai-resources-plugin
eshaben Feb 19, 2026
cc881a8
Merge remote-tracking branch 'origin' into adds-per-page-ai-actions
dawnkelly09 Feb 19, 2026
4ef79cb
Merge remote-tracking branch 'origin/staging-ai-resources-plugin' int…
dawnkelly09 Feb 19, 2026
d483610
patches to scripts to clear up build errors, etc.
dawnkelly09 Feb 19, 2026
d3f4d5b
Merge origin/build-ai-resources-plugin, resolve minify conflict
dawnkelly09 Feb 20, 2026
850b559
Merge build-ai-resources-plugin into adds-per-page-ai-actions
dawnkelly09 Feb 20, 2026
ba0eb8f
moves helper docs into separate folder, updates "page" versus "file" …
dawnkelly09 Feb 20, 2026
44c85a5
Add subpath-aware URL building for ai_page_actions, add tests
dawnkelly09 Feb 23, 2026
67e1073
Add subpath-aware URL building for ai_resources_page, add tests
dawnkelly09 Feb 23, 2026
10967aa
Merge origin/main into staging-ai-resources-plugin
dawnkelly09 Feb 23, 2026
37b8c21
Merge branch 'staging-ai-resources-plugin' into adds-per-page-ai-actions
dawnkelly09 Feb 23, 2026
184e342
update tests to match code patches and changes
dawnkelly09 Feb 23, 2026
d143c1f
Merge pull request #12 from papermoonio/adds-per-page-ai-actions
eshaben Feb 24, 2026
8f4afbc
Revert formatting-only changes to copy_md, page_toggle, and minify tests
dawnkelly09 Feb 24, 2026
11e983b
updates from Copliot reedback
dawnkelly09 Feb 24, 2026
1342cbc
adds index.md to excluded pages so widget won't show for per-page AI …
dawnkelly09 Feb 25, 2026
e6e603c
updates to use llms_config to skip pages/dirs for per page widget
dawnkelly09 Feb 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ We welcome:
Run formatting and linting manually if needed:

```bash
black plugins/
isort plugins/
flake8 plugins/
black plugins/ helper_lib/
isort plugins/ helper_lib/
flake8 plugins/ helper_lib/
```

4. Submit a pull request with a clear title and description.
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,17 @@ A collection of custom [MkDocs](https://www.mkdocs.org/) plugins designed to ext

Currently included:

- **[AI Resources Page](https://github.com/papermoonio/mkdocs-plugins/blob/main/docs/ai-resources-page.md)**: Automate the generation of an "AI Resources" page with a dynamic table of artifact files.
- **[Copy Markdown](https://github.com/papermoonio/mkdocs-plugins/blob/main/docs/copy-md.md)**: Serve raw Markdown files by copying them directly to your site's build folder.
- **[Minify](https://github.com/papermoonio/mkdocs-plugins/blob/main/docs/minify.md)**: Minify HTML, JS, and CSS files globally or by scope to optimize your site's performance.
- **[Page Toggle](https://github.com/papermoonio/mkdocs-plugins/blob/main/docs/page-toggle.md)**: Create variant pages for the same content and display them with an interactive toggle interface.
- **[Resolve Markdown](https://github.com/papermoonio/mkdocs-plugins/blob/main/docs/resolve-md.md)**: Resolve variable and code snippet placeholders and serve resolved Markdown files directly from your site's build folder.
- **[AI Page Actions](https://github.com/papermoonio/mkdocs-plugins/blob/main/docs/ai-page-actions.md)**: Injects a per-page AI actions widget next to each page's H1 heading at build time.

Helper utilities and libraries:

- **[AI File Actions](https://github.com/papermoonio/mkdocs-plugins/blob/main/docs/helper_library/ai-file-actions.md)** *(shared library)*: Convenience wrapper around `ai_file_utils` for generating AI file action dropdowns.
- **[AI File Utils](https://github.com/papermoonio/mkdocs-plugins/blob/main/docs/helper_library/ai-file-utils.md)** *(shared library)*: Resolves action definitions from JSON and generates split-button dropdown HTML for copy, download, view, and LLM tool actions.

## Installation

Expand All @@ -23,6 +30,8 @@ Enable one or more plugins in your `mkdocs.yml`:

```yaml
plugins:
- ai_resources_page
- ai_page_actions
- copy_md:
source_dir: docs/.example
target_dir: example
Expand Down
55 changes: 55 additions & 0 deletions docs/ai-page-actions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# AI Page Actions Plugin

The AI Page Actions plugin injects a per-page AI actions widget (split-button dropdown) next to each page's H1 heading at build time. It reuses the shared [`ai_file_utils`](helper_library/ai-file-utils.md) library (in `helper_lib/ai_file_utils/`) for slug resolution, URL building, action resolution, and HTML generation — the same library that powers the table widget via [`ai_file_actions`](helper_library/ai-file-actions.md).

## Usage

Add the plugin to your `mkdocs.yml`:

```yaml
plugins:
- ai_page_actions
```

The plugin requires no configuration in `mkdocs.yml`. It automatically reads page exclusion rules from the site's `llms_config.json` (the same file used by `resolve_md`). Action definitions are managed in `helper_lib/ai_file_utils/ai_file_actions.json` (see [AI File Utils](helper_library/ai-file-utils.md)).

## How It Works

The plugin uses the `on_post_page` MkDocs hook, which runs on the fully rendered HTML page after all other content hooks (including `page_toggle`) have finished.

For each page:

1. Checks whether the page is excluded via `ai_file_utils.is_page_excluded()` (driven by `llms_config.json` exclusions, dot-directories, and front matter)
2. Parses the rendered HTML with BeautifulSoup to locate the H1 heading inside `.md-content`
3. Builds the slug and URL (e.g., `/ai/pages/{slug}.md`). For sites deployed under a subpath (e.g., `site_url: https://example.com/docs/`), the path prefix is extracted and prepended automatically (e.g., `/docs/ai/pages/{slug}.md`)
4. Generates the widget HTML using `AIFileUtils.generate_dropdown_html()` with `primary_label="Copy page"` and `label_replace={"file": "page"}` so dropdown items read "View page in Markdown" etc.
5. Wraps the H1 and widget in a `<div class="h1-ai-actions-wrapper">` flex container

### Toggle Pages

For pages using the `page_toggle` plugin, the widget handles each variant independently. It finds H1s inside `.toggle-header > span[data-variant]` elements and reads the `data-filename` attribute from the corresponding toggle button to build variant-specific slugs via `AIFileUtils.build_toggle_slug()`.

## Page Exclusions

The plugin determines which pages to skip using the same exclusion rules that `resolve_md` uses to decide which pages get AI artifact files. This ensures the widget is never rendered on pages that have no AI files to serve. Exclusions are checked in the following order:

1. **Dot-directories**: Any page whose source path includes a directory starting with `.` (e.g., `.snippets/`, `.github/`) is always excluded automatically.
2. **`skip_basenames`**: Pages whose filename exactly matches an entry in `content.exclusions.skip_basenames` from `llms_config.json` are excluded (e.g., `README.md`, `LICENSE.md`).
3. **`skip_paths`**: Pages whose source path contains a `content.exclusions.skip_paths` substring from `llms_config.json` are excluded (e.g., `venv`, `node_modules`).
4. **Front matter**: Set `hide_ai_actions: true` in a page's front matter for a one-off override.

```yaml
---
hide_ai_actions: true
---
```

The plugin loads `llms_config.json` from the project root (the directory containing `mkdocs.yml`) during the `on_config` hook. If the file is missing, the plugin logs a warning and falls back to dot-directory and front-matter checks only.

## Styling

The widget uses the same CSS classes as the table widget (`ai-file-actions.css`). The H1 wrapper layout is controlled by `.h1-ai-actions-wrapper`, which includes mobile responsive styles that stack the H1 and widget vertically on small screens.

## Client-Side Behavior

The widget relies on `ai-file-actions.js` for all client-side interactions (copy, download, dropdown toggle, keyboard navigation, analytics). No additional JavaScript is needed.
73 changes: 73 additions & 0 deletions docs/ai-resources-page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# AI Resources Page Plugin

The AI Resources Page plugin automates the generation of an "AI Resources" page for your documentation site. It processes a configuration file (`llms_config.json`) to dynamically build an overview section and a table of resources (global files and category bundles) optimized for LLMs.

## Installation

This plugin is included in the `papermoon-mkdocs-plugins` package.

```bash
pip install papermoon-mkdocs-plugins
```

## Configuration

Add the plugin to your `mkdocs.yml`:

```yaml
plugins:
- ai_resources_page
```

### `llms_config.json`

The plugin relies on an `llms_config.json` file in your project root to determine what content to generate.

Key sections used by this plugin:

- **`project`**:
- `name`: The name of your project (e.g., "Polkadot"). **Required**.
- **`content`**:
- `categories_info`: A dictionary of category metadata. Key order controls the display order in the table. Each value contains `name` and `description`.
- **`outputs`**:
- `public_root`: The URL path where AI artifacts are served (default: `/ai/`).

#### Example Config

```json
{
"project": {
"name": "My Project"
},
"content": {
"categories_info": {
"basics": {
"name": "Basics",
"description": "General knowledge base and overview content."
},
"reference": {
"name": "Reference",
"description": "API references and glossary."
}
}
},
"outputs": {
"public_root": "/ai/"
}
}
```

## How It Works

1. **Detection**: The plugin hooks into the `on_page_markdown` event and looks for a page named `ai-resources.md` (by filename).
2. **Generation**:
* It replaces the page content with a standard Introduction/Overview using the `project.name`.
* It generates a table including:
* **Standard Files**: `llms.txt`, `site-index.json`, `llms-full.jsonl`.
* **Categories**: Iterates through `categories_info` (in key order) to create rows for each category bundle.
3. **Client-side actions**: Each table row includes a split-button dropdown (generated via the shared [`ai_file_utils`](helper_library/ai-file-utils.md) library) with copy, view, download, and LLM tool actions. For sites deployed under a subpath (e.g., `site_url: https://example.com/docs/`), the path prefix is extracted from `site_url` and prepended to all artifact URLs automatically.

## Notes

- This plugin is designed to work in tandem with the `resolve_md` plugin (which generates the actual artifact files) and the `ai_file_utils` shared library (which provides the dropdown UI). Client-side JavaScript (`ai-file-actions.js`) handles the button actions.
- If `project.name` is missing from `llms_config.json`, the build will fail with an error to prevent incorrect branding.
40 changes: 40 additions & 0 deletions docs/helper_library/ai-file-actions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# AI File Actions

`ai_file_actions` is a shared library (in `helper_lib/ai_file_actions/`) that provides a convenience wrapper around [`ai_file_utils`](ai-file-utils.md). It delegates all action resolution and HTML generation to `AIFileUtils`.

This is **not** a standalone MkDocs plugin. It does not need to be added to `mkdocs.yml`.

## Usage

Import and use directly in Python code:

```python
from helper_lib.ai_file_actions.plugin import AiFileActionsPlugin

actions = AiFileActionsPlugin()
html = actions.generate_dropdown_html(
url="/ai/llms-full.jsonl",
filename="llms-full.jsonl",
exclude=["view-markdown"],
)
```

## JSON-Driven Dropdown

Dropdown items are driven by the `ai_file_actions.json` schema in the shared `helper_lib/ai_file_utils/` library. Each action in the JSON defines:

- `type` — `"link"` (opens a URL) or `"clipboard"` (copies content)
- `id` — Unique identifier (e.g., `"view-markdown"`, `"download-markdown"`)
- `label` — Display text for the menu item
- `icon` — Inline SVG markup for the item's icon
- `href` — URL template (link-type actions)
- `download` — Filename for download (link-type actions with download behavior)
- `clipboardContent` — Content template (clipboard-type actions)
- `promptTemplate` — Prompt template for AI tool actions
- `analyticsKey` — Analytics event key

To add, remove, or modify dropdown items, edit `helper_lib/ai_file_utils/ai_file_actions.json`. No code changes are needed.

## Integration

The generated HTML relies on client-side JavaScript (`ai-file-actions.js`) to handle click events and perform the actual actions (fetch, copy, download, open). The JavaScript dispatches based on `data-action-type` attributes (`link` or `clipboard`) set by the library, rather than hardcoded action names.
160 changes: 160 additions & 0 deletions docs/helper_library/ai-file-utils.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
# AI File Utils

The `ai_file_utils` module serves as a centralized "contract" and utility service for defining and resolving actions related to AI artifacts. It allows you to separate the *definition* of actions (like "View Markdown", "Open in ChatGPT") from the *implementation* in the UI.

This is not a standalone MkDocs plugin but a shared library that lives in `helper_lib/ai_file_utils/` (separate from `plugins/`). Other plugins (like `ai_file_actions`, `ai_page_actions`, and `ai_resources_page`) import it to resolve action lists and generate the split-button dropdown HTML for any documentation page.

## 🔹 Usage

Since this is a helper library, you do not need to add it to your `mkdocs.yml` plugins list.

### Using in Python Code

Import the utility class directly in your code.

**`resolve_actions`** takes page context and returns a list of fully resolved action objects:

```python
from helper_lib.ai_file_utils.ai_file_utils import AIFileUtils

utils = AIFileUtils()

actions = utils.resolve_actions(
page_url="/ai/pages/my-page.md",
filename="my-page.md",
content="# My Page Content...",
prompt_page_url="https://docs.example.com/ai/pages/my-page.md", # optional
)
```

**`generate_dropdown_html`** renders the split-button dropdown component:

```python
from helper_lib.ai_file_utils.ai_file_utils import AIFileUtils

utils = AIFileUtils()

html = utils.generate_dropdown_html(
url="/ai/pages/my-page.md",
filename="my-page.md",
exclude=["view-markdown"], # optional
primary_label="Copy page", # optional
)
```

**Parameters:**

- `url` (str): The URL of the file to act upon.
- `filename` (str): The filename for the download action.
- `exclude` (list | None, optional): Action IDs to exclude from the dropdown. Defaults to `None` (all actions shown).
- `primary_label` (str | None, optional): Override the primary button's label. Defaults to `None` (uses the label from JSON, e.g., "Copy file").
- `site_url` (str, optional): The base site URL (e.g., `"https://docs.polkadot.com/"`). When provided, the fully-qualified URL is built and passed to prompt templates so external services (ChatGPT, Claude) receive a complete address. Defaults to `""`.
- `label_replace` (dict | None, optional): String replacements to apply to dropdown item labels. For example, `{"file": "page"}` changes "View file in Markdown" to "View page in Markdown". Defaults to `None` (labels used as-is from JSON).

**Returns:** An HTML string containing the split-button dropdown. The action marked `primary: true` in the JSON renders as the left-side button; all other actions render as dropdown items.

### Slug and URL Helpers

Static methods for building slugs and AI page URLs. These mirror the logic in `resolve_md.compute_slug_and_url` and the client-side `buildSlugFromPath`.

```python
slug = AIFileUtils.build_slug("develop/toolkit/")
# => "develop-toolkit"

slug = AIFileUtils.build_toggle_slug("develop/toolkit/", "python")
# => "develop-python"

url = AIFileUtils.build_ai_page_url("develop-toolkit")
# => "/ai/pages/develop-toolkit.md"
```

### Page Exclusion

The `is_page_excluded` method checks whether a page should be excluded from widget injection. It accepts the page's source path, front matter metadata, and optional skip lists from `llms_config.json`:

```python
excluded = utils.is_page_excluded(
page.file.src_path,
page.meta,
skip_basenames=["README.md", "LICENSE.md"],
skip_paths=["venv", "node_modules"],
)
```

Exclusions are checked in order:

1. **Dot-directories** — any path component starting with `.` (always applied)
2. **`skip_basenames`** — exact filename match
3. **`skip_paths`** — substring match against the source path
4. **Front matter** — the key defined in `pageWidget.frontMatterKey` (default: `hide_ai_actions`)

## Page Widget Configuration

The `pageWidget` section of `ai_file_actions.json` controls front-matter-based exclusion:

```json
{
"pageWidget": {
"frontMatterKey": "hide_ai_actions"
}
}
```

| Field | Type | Description |
| :--- | :--- | :--- |
| `frontMatterKey` | `string` | Front matter key that, when truthy, excludes a page from widget injection. |

Path-based exclusions (`skip_basenames` and `skip_paths`) are defined in `llms_config.json` under `content.exclusions` and passed in by the calling plugin (e.g., `ai_page_actions`).

## Action Model

The core of this module is the **Action Model**, defined in `ai_file_actions.json`. This JSON schema defines what actions are available and how they behave.

### Schema Fields

| Field | Type | Description |
| :--- | :--- | :--- |
| `type` | `string` | The category of action. Currently supports `link` (navigation) and `clipboard` (copy text). |
| `id` | `string` | Unique identifier (e.g., `view-markdown`, `open-chat-gpt`). |
| `label` | `string` | The human-readable text displayed in the UI. |
| `analyticsKey` | `string` | A standardized key for tracking usage events. |
| `href` | `string` | (Link only) The destination URL. Supports interpolation. |
| `download` | `string` | (Link only) If present, triggers a file download with this filename. |
| `clipboardContent` | `string` | (Clipboard only) The text to be copied. |
| `promptTemplate` | `string` | (LLM only) A template used to generate the `{{ prompt }}` variable. |
| `icon` | `string` | Inline SVG markup for the action's icon. |
| `trailingIcon` | `string` | Inline SVG markup rendered after the label (e.g., external-link arrow). |
| `primary` | `boolean` | If `true`, renders as the left-side button instead of a dropdown item. |

### Interpolation Variables

The module supports dynamic injection of context using `{{ variable }}` syntax.

| Variable | Description |
| :--- | :--- |
| `{{ page_url }}` | The URL to the resolved AI artifact. In prompt templates, this is the fully-qualified `prompt_page_url` (e.g., `https://docs.example.com/ai/pages/my-page.md`). In other fields (`href`, `download`), it is the relative path. |
| `{{ filename }}` | The filename of the markdown file (e.g., `basics.md`). |
| `{{ content }}` | The full text content of the markdown file. |
| `{{ prompt }}` | A special variable generated by processing the `promptTemplate` and URL-encoding the result. |

## 🔹 Adding New Actions

To add a new action (e.g., "Open in Gemini"), you modify `helper_lib/ai_file_utils/ai_file_actions.json`. You do not need to write new Python code.

### Example: Adding a New LLM

```json
{
"type": "link",
"id": "open-gemini",
"label": "Open in Gemini",
"href": "https://gemini.google.com/app?q={{ prompt }}",
"promptTemplate": "Read {{ page_url }} and explain it to me.",
"analyticsKey": "open_page_markdown_gemini"
}
```

This configuration will automatically:
1. Resolve `{{ page_url }}` in the prompt template (using the fully-qualified `prompt_page_url`).
2. URL-encode the result into `{{ prompt }}`.
3. Inject it into the `href`.
Loading