Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .vscodeignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ assets/simple-start.gif
assets/test-images
assets/button.svg
assets/extension-demo.gif
assets/readme-*.png
scripts/
AGENTS.md
.prettierrc.json
Expand Down
11 changes: 11 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ Match the maintainer's request to a workflow in [docs/workflows.md](docs/workflo
- Push, tag, merge, and publish only when the maintainer says so.
- Add no `Co-Authored-By` trailers and no tool or model attribution to commits, pull requests, files, or release notes.

## Tools and Authority

`gh`, `vsce`, and `ovsx` are installed and signed in. The full text is in [docs/sibling-extensions.md](docs/sibling-extensions.md#tools-and-authority).

- **Read with the tools:**
- Fetch GitHub, Marketplace, and Open VSX state yourself, and do not ask for approval or for a web page.
- **Change state on the maintainer's word:**
- Merge, push, tag, and publish wait for the maintainer. After they say so, run each step without asking again.
- **Never handle tokens:**
- Do not read, print, store, or paste one. If a tool or sign-in is missing, ask the maintainer.

## Multi-file change protocol

After an edit that touches several files, align code, tests, and documents before committing.
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

All notable changes to this project will be documented in this file.

## [2.2.1] - 2026-10-26

### Fixed

- A Mermaid-only file no longer treats a `<!-- slide -->` line shown inside a code block as a slide marker.
- A `::: mermaid` diagram that uses the class shorthand `A:::name` is no longer cut short.
- A Mermaid fence quoted inside another code block is no longer shown as a diagram slide.
- A diagram with a syntax error now shows its source below the error note, instead of Mermaid's error graphic.

### Changed

- The README shows screenshots of each feature instead of an animated GIF.

## [2.2.0] - 2026-10-25

### Changed
Expand Down
Binary file added assets/readme-appearance-settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/readme-hero-dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/readme-markdown-rendering.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/readme-mermaid-diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/readme-slide-markers.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/readme-theme-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 20 additions & 2 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,16 @@ Mermaid-only mode keeps files written for the original `mermaid-slideshow` exten
- Stray closing tags and repeated opening tags are ignored.
- Delimiters inside fenced code blocks (triple backticks or `::: mermaid`) are ignored.
- A leading YAML front matter block is removed before processing.
- `hasSlideDelimiter()` applies the same rule when it picks the mode, so a delimiter shown in a code sample does not turn a Mermaid-only file into a slide file.

### Mermaid syntaxes

Two fence syntaxes work in both modes: backtick fences (```` ```mermaid ````) and Azure DevOps fences (`::: mermaid`). `extractMermaidBlocks()` finds both in one regex pass, so diagrams keep their document order.
Two fence syntaxes work in both modes: backtick fences (```` ```mermaid ````) and Azure DevOps fences (`::: mermaid`). `extractMermaidBlocks()` finds both in one line-by-line pass, so diagrams keep their document order.

- **Fence rules:**
- A fence opens at the start of a line, and a `:::` block closes only at a `:::` line of its own, as in the renderer.
- A Mermaid fence quoted inside another code block is sample text, not a diagram, and the class shorthand `A:::hot` stays inside its diagram.
- Longer backtick fences and `~~~` fences are not recognized. [decisions.md](decisions.md#longer-or-tilde-code-fences) records why.

## Webview rendering

Expand Down Expand Up @@ -78,8 +84,9 @@ The template holds two scripts and one stylesheet.
- **Isolation:**
- A failed load shows a note and the diagram source on that slide. Text slides, navigation, and live update keep working.
- Each diagram renders on its own, so one syntax error leaves the others intact.
- A syntax error shows a note and the diagram source. Mermaid draws no error graphic of its own, and the page restores the source text because a failed run can leave the node empty.
- **Configuration:**
- The module sets `startOnLoad` and `theme` only. The library default applies for everything else, including `securityLevel`, which is `strict`.
- The module sets `startOnLoad`, `suppressErrorRendering`, and `theme` only. The library default applies for everything else, including `securityLevel`, which is `strict`.

### Content Security Policy

Expand All @@ -94,6 +101,17 @@ The template holds two scripts and one stylesheet.

The nonce comes from `crypto.randomBytes` and changes on every render.

**Review finding (2026-10-26):** the policy has no gap that lets document text run script, and no change is made.

- **Scripts:**
- `script-src` names a directory of one pinned Mermaid version, not the whole CDN host, so a raw `<script src>` from jsDelivr cannot run even if it reached the page. It cannot reach the page, because renderer output escapes slide text and only `<img>` is built from a URL.
- A test pins the directory scope and the nonce on both scripts.
- **Messages to the host:**
- The page never calls `postMessage`. It receives `update` messages and stores the slide position with `setState`.
- A message handler on the host is a new surface. Add one only with a scheme allow-list and tests, as [decisions.md](decisions.md#links-in-slide-text) describes.
- **Images:**
- `img-src https:` accepts any host, because decks name their own image hosts. A deck that embeds a remote image tells that host the viewer's IP address. [decisions.md](decisions.md#image-hosts-in-the-content-security-policy) records the trade-off.

### Image support

- **Supported:**
Expand Down
14 changes: 13 additions & 1 deletion docs/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Related guides: [architecture.md](architecture.md) for the design these decision
| Browser-driven automated tests | Defer | Renderer regressions escape the unit tests twice |
| Presenter mode, notes, export | Reject | A concrete presenting workflow cannot be met otherwise |
| Settings for Mermaid layout and look | Reject | The library's defaults stop fitting VS Code themes |
| Image hosts in the Content Security Policy | Reject | A privacy requirement rules out remote images |

## Entries

Expand Down Expand Up @@ -90,7 +91,7 @@ Related guides: [architecture.md](architecture.md) for the design these decision

- **What it is:**
- Markdown lets a fence use four or more backticks, or `~~~`, so a code sample can contain a three-backtick fence.
- Both the extractor and the renderer toggle on any line that starts with three backticks. A sample wrapped in four backticks that contains a three-backtick fence ends early.
- The extractor, the mode check, and the renderer all toggle on any line that starts with three backticks. A sample wrapped in four backticks that contains a three-backtick fence ends early.
- **Why it is deferred:**
- It affects only decks that show Markdown syntax inside a slide. The fix needs the same fence-length rule in two places and tests for both.
- **Reopen when:**
Expand Down Expand Up @@ -141,3 +142,14 @@ Related guides: [architecture.md](architecture.md) for the design these decision
- The dependency policy keeps library visual defaults. Each setting adds a surface to test and document and ties the extension to one library version's option names.
- **Reopen when:**
- The library defaults stop fitting VS Code themes.

### Image hosts in the Content Security Policy

- **What it is:**
- Replacing `img-src https:` with a list of allowed image hosts.
- Reviewed on 2026-10-26 with the rest of the policy ([architecture.md](architecture.md#content-security-policy)).
- **Why it is rejected:**
- Decks point at the image hosts their authors use, so a list would break real decks or grow without end.
- The risk is a request to a host the author chose. It carries no document text, and the extension never loads an image on its own.
- **Reopen when:**
- A user needs decks that make no third-party request. A setting that turns remote images off is the lighter answer.
23 changes: 20 additions & 3 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ This guide covers setup, daily checks, continuous integration, and the release p
| `examples/02-slide-mode-basics.md` | Slide mode happy path: text and diagrams, both fence syntaxes |
| `examples/03-slide-mode-advanced.md` | Slide mode edge cases: preamble, gaps, empty pairs, a tall slide |
| `examples/04-demo-script.md` | Walkthrough content for demos and manual testing |
| `examples/showcase.md` | Source of the README screenshots |

## Prerequisites

Expand Down Expand Up @@ -61,6 +62,22 @@ code --uninstall-extension KunalPathak.markdown-presentation-tool

The `.vsix` file is git-ignored. Do not commit it.

### Readme images

- **Link:**
- Link each image by an absolute `https://raw.githubusercontent.com/kanad13/markdown-presentation-tool/main/assets/<file>.png` URL.
- The package leaves `assets/readme-*.png` out, so a relative link breaks on the Marketplace and Open VSX pages. A `github.com/.../blob/...` link returns an HTML page, not the image.
- **Format:**
- Use PNG, because the Marketplace rejects SVG. Name the files `assets/readme-*.png`.
- Give every image alt text of one full sentence that describes what it shows.
- **Merge order:**
- Add the file on the same branch as the link. The image URL works for readers only after the branch merges to `main`.
- **Source:**
- `examples/showcase.md` is the document behind the screenshots. Serve it with the [browser harness](testing.md#browser-harness-procedure) and capture each slide in a 1100 by 700 viewport, then crop. On macOS, `sips -c H W --cropOffset Y X` ignores a zero offset and crops from the centre, so use an offset of 1.
- Open every cropped image before you link it.
- **Check:**
- The `readme images` suite in `test/docs.test.js` checks the URLs, the files, the format, the alt text, and a minimum of four images.

### Check rendering in a browser

`node scripts/render-harness.js` serves the generated page on `localhost`. The procedure is in [testing.md](testing.md#browser-harness-procedure).
Expand Down Expand Up @@ -148,7 +165,7 @@ git tag -a vX.Y.Z -m "Release version X.Y.Z"
git push origin vX.Y.Z
```

The tag push starts the release workflow. It checks that the tag is on `main` and matches `package.json` and the changelog entry, runs the tests, packages the extension, creates a GitHub Release with the `.vsix` attached, and publishes to Open VSX. The release workflow publishes to Open VSX. It signs in with a short-lived OIDC token that Open VSX issues to the registered trusted publisher (repository `kanad13/markdown-presentation-tool`, workflow `release.yml`), so no stored token is needed. The repository secret `OVSX_PAT` is a fallback: when it is set, the workflow uses it instead of OIDC. Delete it after a release has published through OIDC.
The tag push starts the release workflow. Never re-push an old tag, because it would run that release again. It checks that the tag is on `main` and matches `package.json` and the changelog entry, runs the tests, packages the extension, creates a GitHub Release with the `.vsix` attached, and publishes to Open VSX. The release workflow publishes to Open VSX. It signs in with a short-lived OIDC token that Open VSX issues to the registered trusted publisher (repository `kanad13/markdown-presentation-tool`, workflow `release.yml`), so no stored token is needed. The repository secret `OVSX_PAT` is a fallback: when it is set, the workflow uses it instead of OIDC. Delete it after a release has published through OIDC.

If the run fails after the tag exists, fix the cause and start the workflow again from the Actions tab (**Release > Run workflow**, from `main`, with the tag). Open VSX ignores a version it already has, so repeating a run is safe. Tags created before this scheme use the `mpt-v` prefix. They stay as history and are not published again.

Expand All @@ -161,7 +178,7 @@ npm ci
npm run publish -- --pat <VSCE_PAT>
```

Then confirm that the Marketplace listing shows the new version (allow for propagation delay), that the release exists with its asset, and that CI is green.
Then confirm each result with a command, not a polling loop: `vsce show KunalPathak.markdown-presentation-tool` for the Marketplace (the page lags 5 to 10 minutes), `ovsx show KunalPathak.markdown-presentation-tool` for Open VSX, and `gh release view vX.Y.Z` for the release and its asset. CI must be green.

## Troubleshooting

Expand All @@ -174,6 +191,6 @@ Then confirm that the Marketplace listing shows the new version (allow for propa
| Release fails: invalid tag | Use exactly `vX.Y.Z`. `1.2.1`, `v1.2`, and `v1.2.1-beta` are rejected |
| Release fails: version mismatch | The tag must equal `v` plus the `package.json` version. Fix, merge, and tag the new commit |
| Release fails: missing changelog entry | Add the `## [X.Y.Z]` header, merge, and tag the new commit. Ask before moving a pushed tag |
| Open VSX publish fails | Run the workflow again with the tag. A version Open VSX already has is skipped |
| Open VSX publish fails | Open VSX can be read-only. Run `gh workflow run Release --ref main -f tag=vX.Y.Z` again. A version it already has is skipped |
| Marketplace publish fails | The token is usually invalid or expired. Create a new `VSCE_PAT` and run the publish command again |
| CI passed, nothing published | Expected. CI never publishes |
23 changes: 22 additions & 1 deletion docs/sibling-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Some files must be identical in every repository. Others share a purpose and dif
| `.github/workflows/ci.yml` | Identical in all five, except that Safe PDF Viewer adds a browser test step. |
| `.nvmrc` | Holds the lowest Node version that `engines.node` allows. The value differs per repository. CI fails when the two disagree. |
| `docs/workflows.md`, routing table in `AGENTS.md` | Same structure and wording. Rows and steps that name this extension's own tools differ. |
| `docs/sibling-extensions.md` | Same content in all five repositories. |
| `docs/sibling-extensions.md` | Same content in all five repositories. |
| `eslint.config.js`, Prettier config, `.vscodeignore`, `package.json` scripts and tool versions | Aligned in intent. They differ where an extension needs a different setting. |

## When to check the siblings
Expand Down Expand Up @@ -57,3 +57,24 @@ done
- **Stop for approval:**
- Do not push, merge, tag, or release a sibling without the maintainer's instruction.
- Report what changed in each sibling and which siblings need no change.

## Tools and authority

Three command-line tools are installed and signed in on the maintainer's machine. Look up each command with `--help` before you rely on this list.

- **Tools:**
- `gh` (GitHub CLI): check with `gh auth status`.
- `vsce` (Visual Studio Marketplace): check with `vsce verify-pat KunalPathak`.
- `ovsx` (Open VSX): check with `ovsx verify-pat KunalPathak`, which reads the token from the `OVSX_PAT` variable. Without it, `ovsx show KunalPathak.<name>` still reads the registry.
- **Reading needs no approval:**
- Use the tools for every GitHub, Marketplace, and Open VSX read: repository, release, issue, pull request, workflow, and registry state.
- Do not ask the maintainer to open a web page for something a tool can print.
- Examples: `gh release view`, `gh run list`, `gh pr view`, `vsce show KunalPathak.<name>`, `ovsx show KunalPathak.<name>`.
- **Changing state needs the maintainer's word:**
- Merge, push, tag, and publish still happen only when the maintainer says so (see "Git and Commits" in `AGENTS.md`).
- After the maintainer has said so, run every step with the tools and do not ask again for each command.
- **Missing tool or sign-in:**
- Install the tool (`brew install gh`, `npm install -g @vscode/vsce ovsx`), then ask the maintainer to sign in.
- The agent never reads, prints, stores, or pastes a token.
- **Permission prompts:**
- This text does not change the prompts of an agent harness. In Claude Code, they come from `settings.json`, which the maintainer edits.
4 changes: 4 additions & 0 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ Each row names a workflow a user performs, the evidence that protects it, and an
| Hostile setting values | `test/extension.test.js`: allow-lists and the color sanitizer | None |
| Hostile slide text | `test/webview.test.js` (escaping), `test/extension.test.js` (placeholders and `$` patterns, `<` in JSON) | None |
| Read rendered Markdown | `test/webview.test.js` runs the shipped renderer block | Visual styling (harness) |
| Mermaid fence quoted in code, `A:::class` shorthand | `test/extension.test.js`: code-aware extraction and mode detection | None |
| Bad diagram on a slide | Page-contract tests pin the error settings; harness step 6 shows the note and the source | Rendering is manual |
| Keep the page contract | `test/extension.test.js`: policy before scripts, nonce on both scripts, renderer before module, Mermaid options, stale-render guard | Runtime order in a real webview (harness) |
| See diagrams | Harness: every example deck, light and dark | Automated browser test is not built |
| Present with the CDN unreachable | Harness with an unreachable version; template test that the import is dynamic | Manual |
| Keep the slide position across reloads | `test/lifecycle.test.js`: the document key reaches the generated page; `test/extension.test.js`: key escaping | `getState`/`setState` in a real webview (manual) |
Expand Down Expand Up @@ -75,6 +78,7 @@ The harness serves the exact page that the extension generates, with a stand-in
3. Step through every slide with the arrow keys. Every diagram renders and the console shows no errors.
4. Repeat for each file in `examples/` in both themes.
5. To check the offline path, change `MERMAID_VERSION` temporarily to a version that does not exist, restart the harness, and confirm that text slides render and each diagram slide shows a note with the source. Restore the constant afterward.
6. To check a syntax error, add a slide with a broken diagram (for example `graph TD` followed by `A --> --> B [`). Confirm that the slide shows a red note and the diagram source, that Mermaid's bomb graphic does not appear, and that the next slide still renders.

Run this procedure whenever `MERMAID_VERSION`, the Content Security Policy, the renderer, or the stylesheet changes.

Expand Down
Loading
Loading