Merge remote-tracking branch 'origin/main' into codex/pr32-final
# Conflicts: # .github/workflows/ci.yml
This commit is contained in:
@@ -0,0 +1,66 @@
|
|||||||
|
name: Bug report
|
||||||
|
description: A diagram, script, or import path misbehaves — report it so we can fix it.
|
||||||
|
title: "[Bug]: "
|
||||||
|
labels: ["bug"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for reporting. Before submitting, check that this isn't an already-open issue.
|
||||||
|
- type: dropdown
|
||||||
|
id: area
|
||||||
|
attributes:
|
||||||
|
label: Area
|
||||||
|
description: What part of the project is affected?
|
||||||
|
options:
|
||||||
|
- A diagram example
|
||||||
|
- Style guide / skin / linter
|
||||||
|
- draw.io import
|
||||||
|
- Mermaid import
|
||||||
|
- Export (SVG / PNG)
|
||||||
|
- Documentation
|
||||||
|
- CI / tooling
|
||||||
|
- Other
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: What happened?
|
||||||
|
description: Describe the unexpected behavior. Include what you expected instead.
|
||||||
|
placeholder: "The linter accepts a diagram that breaks the accessible SVG contract…"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: repro
|
||||||
|
attributes:
|
||||||
|
label: Steps to reproduce
|
||||||
|
description: Concrete steps — commands, file names, and inputs matter here.
|
||||||
|
placeholder: |
|
||||||
|
1. Run `python3 scripts/lint-skin.py <file>`
|
||||||
|
2. …
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: input
|
||||||
|
id: environment
|
||||||
|
attributes:
|
||||||
|
label: Environment
|
||||||
|
description: OS, Python version, agent (Claude Code / Codex / Pi) if relevant
|
||||||
|
placeholder: "macOS 14, Python 3.11, Claude Code"
|
||||||
|
- type: checkboxes
|
||||||
|
id: gates
|
||||||
|
attributes:
|
||||||
|
label: Validation gates
|
||||||
|
description: If relevant, which gates have you already run?
|
||||||
|
options:
|
||||||
|
- label: "`python3 scripts/test-lint-a11y.py`"
|
||||||
|
- label: "`python3 scripts/lint-skin.py --all --baseline`"
|
||||||
|
- label: "`python3 scripts/verify-drawio-import.py`"
|
||||||
|
- label: "`python3 scripts/verify-mermaid-import.py`"
|
||||||
|
- label: "I haven't run any gates"
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Relevant output
|
||||||
|
description: Paste error messages or linter findings, trimmed to what matters.
|
||||||
|
render: text
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
blank_issues_enabled: true
|
||||||
|
contact_links:
|
||||||
|
- name: Security vulnerability
|
||||||
|
url: https://github.com/cathrynlavery/diagram-design/security/advisories/new
|
||||||
|
about: Report a suspected vulnerability privately — do not open a public issue for it.
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
name: Feature request
|
||||||
|
description: Suggest a new diagram type, primitive, or improvement.
|
||||||
|
title: "[Feature]: "
|
||||||
|
labels: ["enhancement"]
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Ideas are welcome. Before submitting, check the README's "When *not* to use this skill" section — sometimes the right answer is a table, not a diagram.
|
||||||
|
- type: textarea
|
||||||
|
id: problem
|
||||||
|
attributes:
|
||||||
|
label: What are you trying to show?
|
||||||
|
description: Describe the situation and what the reader needs to understand. A sketch or ASCII mock helps a lot.
|
||||||
|
placeholder: "I want to show how... and the reader should see..."
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: proposal
|
||||||
|
attributes:
|
||||||
|
label: Proposed solution
|
||||||
|
description: How could the skill serve this? A new type, a primitive, a change to an existing one, docs?
|
||||||
|
placeholder: "A new `type-*.md` for …, or an extension of …"
|
||||||
|
- type: dropdown
|
||||||
|
id: scope
|
||||||
|
attributes:
|
||||||
|
label: Scope
|
||||||
|
description: Roughly how much surface does this touch?
|
||||||
|
options:
|
||||||
|
- New diagram type (reference + 3 examples + gallery)
|
||||||
|
- New or changed primitive
|
||||||
|
- Import grammar support
|
||||||
|
- Tooling / scripts / CI
|
||||||
|
- Documentation only
|
||||||
|
- Not sure yet — help me scope it
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: checkboxes
|
||||||
|
id: philosophy
|
||||||
|
attributes:
|
||||||
|
label: Design-system fit
|
||||||
|
description: Features in this project must fit the editorial philosophy in SKILL.md §1–§2.
|
||||||
|
options:
|
||||||
|
- label: "I've checked the anti-patterns and 'When not to use' — this still seems worth drawing"
|
||||||
|
- label: "I'm willing to contribute the implementation or pair with a maintainer"
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
## What does this PR do?
|
||||||
|
|
||||||
|
<!-- One or two sentences: what changed and why. -->
|
||||||
|
|
||||||
|
## Validation gates
|
||||||
|
|
||||||
|
<!-- Mark what you ran and that it passed. CI runs all of these on the PR too. -->
|
||||||
|
|
||||||
|
- [ ] `python3 scripts/test-lint-a11y.py`
|
||||||
|
- [ ] `python3 scripts/lint-skin.py --all --baseline`
|
||||||
|
- [ ] `python3 scripts/verify-sequence-oauth.py`
|
||||||
|
- [ ] `python3 scripts/verify-drawio-import.py`
|
||||||
|
- [ ] `python3 scripts/verify-mermaid-import.py`
|
||||||
|
- [ ] `git diff --exit-code` green after `python3 scripts/build-icons.py` (if icons changed)
|
||||||
|
- [ ] Docs updated in the same PR where behavior changed
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
- [ ] No new entries added to `scripts/lint-skin-baseline.txt`
|
||||||
|
- [ ] Generated and source files are consistent (extractor ↔ verifier ↔ reference ↔ command)
|
||||||
|
- [ ] Accessible SVG contract satisfied for new/changed examples
|
||||||
|
- [ ] Code of Conduct respected
|
||||||
|
|
||||||
|
## Related issues
|
||||||
|
|
||||||
|
<!-- Link any issues this closes, e.g. "Closes #12". -->
|
||||||
@@ -32,6 +32,12 @@ jobs:
|
|||||||
- name: Verify URL SVG normalization
|
- name: Verify URL SVG normalization
|
||||||
run: python3 scripts/test-build-icons-quoted-attributes.py
|
run: python3 scripts/test-build-icons-quoted-attributes.py
|
||||||
|
|
||||||
|
- name: Verify Devicon normalization
|
||||||
|
run: python3 scripts/test-build-icons-devicon.py
|
||||||
|
|
||||||
|
- name: Verify mojibake repair utility
|
||||||
|
run: python3 scripts/test-fix-mojibake.py
|
||||||
|
|
||||||
- name: Run skin linter
|
- name: Run skin linter
|
||||||
run: python3 scripts/lint-skin.py --all --baseline
|
run: python3 scripts/lint-skin.py --all --baseline
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
name: Deploy gallery to GitHub Pages
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pages: write
|
||||||
|
id-token: write
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: pages
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
|
||||||
|
- name: Configure Pages
|
||||||
|
uses: actions/configure-pages@v6
|
||||||
|
|
||||||
|
- name: Upload gallery artifact
|
||||||
|
uses: actions/upload-pages-artifact@v5
|
||||||
|
with:
|
||||||
|
path: skills/diagram-design/assets
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
needs: build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
environment:
|
||||||
|
name: github-pages
|
||||||
|
url: ${{ steps.deployment.outputs.page_url }}
|
||||||
|
steps:
|
||||||
|
- name: Deploy to GitHub Pages
|
||||||
|
id: deployment
|
||||||
|
uses: actions/deploy-pages@v5
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, caste, color, religion, or sexual
|
||||||
|
identity and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
- Demonstrating empathy and kindness toward other people
|
||||||
|
- Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
- Giving and gracefully accepting constructive feedback
|
||||||
|
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
- Focusing on what is best not just for us as individuals, but for the overall
|
||||||
|
community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
- The use of sexualized language or imagery, and sexual attention or advances of
|
||||||
|
any kind
|
||||||
|
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
- Public or private harassment
|
||||||
|
- Publishing others' private information, such as a physical or email address,
|
||||||
|
without their explicit permission
|
||||||
|
- Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official email address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
[github.com/cathrynlavery/diagram-design/issues](https://github.com/cathrynlavery/diagram-design/issues)
|
||||||
|
or via private vulnerability reporting ([SECURITY.md](SECURITY.md) links the
|
||||||
|
private path). All complaints will be reviewed and investigated promptly and
|
||||||
|
fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series of
|
||||||
|
actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or permanent
|
||||||
|
ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within the
|
||||||
|
community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.1, available at
|
||||||
|
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by
|
||||||
|
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
|
||||||
|
[https://www.contributor-covenant.org/translations][translations].
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
|
||||||
|
[Mozilla CoC]: https://github.com/mozilla/diversity
|
||||||
|
[FAQ]: https://www.contributor-covenant.org/faq
|
||||||
|
[translations]: https://www.contributor-covenant.org/translations
|
||||||
+136
@@ -0,0 +1,136 @@
|
|||||||
|
# Contributing to Diagram Design
|
||||||
|
|
||||||
|
Thanks for wanting to contribute — this project only gets better with more eyes on it.
|
||||||
|
|
||||||
|
Please read [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) first. All contributions are expected to keep the community welcoming.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## What this project is
|
||||||
|
|
||||||
|
Diagram Design is an agent skill (Claude Code, Codex, Pi) that produces editorial-quality diagrams as self-contained HTML files. The repo is documentation-first: `skills/diagram-design/SKILL.md` is the index, each of the 27 diagram types has its own reference file, and the extractor scripts in `skills/diagram-design/scripts/` turn draw.io and Mermaid sources into a structured IR.
|
||||||
|
|
||||||
|
See [README.md](README.md) for the full picture, including the design system and the import/export flows.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Before you start
|
||||||
|
|
||||||
|
- **Create an issue first** for anything non-trivial (new type, behavior change, import grammar work). Small fixes and docs can go straight to a PR.
|
||||||
|
- **Work on a branch** — never commit directly to `main`.
|
||||||
|
- **Keep the scope tight.** One PR = one concern. Mixing a new diagram type with a docs rewrite makes review slow.
|
||||||
|
- **Python 3.10+ is required** for the development scripts (CI runs 3.11).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Validation gates
|
||||||
|
|
||||||
|
Every validation gate below must pass before a PR is ready. They also run automatically as GitHub Actions CI (`.github/workflows/ci.yml`).
|
||||||
|
|
||||||
|
| What it checks | Command |
|
||||||
|
|---|---|
|
||||||
|
| Accessible SVG contract (unit tests for the a11y linter) | `python3 scripts/test-lint-a11y.py` |
|
||||||
|
| Skin conformance of every example (colors, fonts, a11y) | `python3 scripts/lint-skin.py --all --baseline` |
|
||||||
|
| A single file, e.g. a new example | `python3 scripts/lint-skin.py skills/diagram-design/assets/example-my-type.html` |
|
||||||
|
| Sequence-doc consistency (ATL fragments, budgets) | `python3 scripts/verify-sequence-oauth.py` |
|
||||||
|
| draw.io import path (real extractor vs fixtures + docs sync) | `python3 scripts/verify-drawio-import.py` |
|
||||||
|
| Mermaid import path (grammars, adversarial input, caps, docs sync) | `python3 scripts/verify-mermaid-import.py` |
|
||||||
|
| Generated icon assets are up to date (`icons.html`, `primitive-icons.md`) | `python3 scripts/build-icons.py` then `git diff --exit-code` on the two generated files |
|
||||||
|
|
||||||
|
Run them all at once before pushing:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/test-lint-a11y.py \
|
||||||
|
&& python3 scripts/lint-skin.py --all --baseline \
|
||||||
|
&& python3 scripts/verify-sequence-oauth.py \
|
||||||
|
&& python3 scripts/verify-drawio-import.py \
|
||||||
|
&& python3 scripts/verify-mermaid-import.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### If a gate fails
|
||||||
|
|
||||||
|
- **`lint-skin.py`:** the failure message names the file, line, and category (`color`, `font-family`, `a11y`, `external-asset`, `pure-black`, `script`). Colors must come from the palette in `skills/diagram-design/references/style-guide.md`; fonts from the allowed list; diagrams must satisfy the accessible SVG contract (see below).
|
||||||
|
- **`verify-*.py`:** the extractor's real behavior no longer matches its fixture or the documentation, or the reference/command/prompt wiring drifted. Fix the source of truth — do not widen a test to avoid a failure.
|
||||||
|
- **Icon assets:** you changed `scripts/vendor/icons/` or `scripts/build-icons.py` and the generated files went stale. Rerun `python3 scripts/build-icons.py` and commit the regenerated files.
|
||||||
|
|
||||||
|
Do **not** add a file to `scripts/lint-skin-baseline.txt` to get your example through. The baseline exists only for legacy pre-2.0 examples that legitimately predate the current skin, and it still receives a11y checks.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The accessible SVG contract (a11y)
|
||||||
|
|
||||||
|
Every diagram `<svg>` must satisfy the contract enforced by the linter:
|
||||||
|
|
||||||
|
1. `role="img"` and `aria-labelledby` naming the `<title>` **and** `<desc>`.
|
||||||
|
2. `<title>` is the **first child** of `<svg>` (before `<defs>`).
|
||||||
|
3. IDs are prefixed per diagram and variant: `<slug>-title` / `<slug>-desc` — for `example-loop-dark.html` the slug is `loop-dark`, so the IDs are `loop-dark-title` / `loop-dark-desc`. Bare `title`/`desc` IDs and duplicate IDs are rejected.
|
||||||
|
4. `<title>` is the short subject name (≈ the `<h1>`, ≤ 60 chars); `<desc>` is one sentence describing the *content*, not the geometry.
|
||||||
|
5. Purely decorative SVGs (`aria-hidden="true"`) are exempt.
|
||||||
|
|
||||||
|
The contract lives in `scripts/lint-skin.py` (`lint_accessible_svgs`) and is unit-tested by `scripts/test-lint-a11y.py`. When in doubt, pattern-match an existing example.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Working on examples (diagrams)
|
||||||
|
|
||||||
|
Every diagram type ships three variants: minimal light (`example-<type>.html`), minimal dark (`example-<type>-dark.html`), and full editorial (`example-<type>-full.html`).
|
||||||
|
|
||||||
|
1. Copy the closest template (`skills/diagram-design/assets/template.html`, `template-dark.html`, or `template-full.html`).
|
||||||
|
2. Load the matching `references/type-<name>.md` and follow its layout conventions.
|
||||||
|
3. Replace the eyebrow, h1, and SVG body; replace the `[diagram-slug]` placeholders with your file's slug and keep the `<title>`/`<desc>` slots filled.
|
||||||
|
4. Run the taste gate in `SKILL.md` §9, then the linter:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/lint-skin.py skills/diagram-design/assets/example-my-type.html
|
||||||
|
```
|
||||||
|
|
||||||
|
New examples should be added to the gallery (`assets/index.html`) so they stay browsable.
|
||||||
|
|
||||||
|
## Adding a new diagram type
|
||||||
|
|
||||||
|
1. Write `skills/diagram-design/references/type-<name>.md` — layout conventions, anti-patterns, and a worked pattern for that type. Mirror an existing reference's structure.
|
||||||
|
2. Add the row to the selection table in `skills/diagram-design/SKILL.md` §3.
|
||||||
|
3. Add the three example variants (see above) and register them in the gallery.
|
||||||
|
4. Run the full gate suite — new examples are linted automatically by `--all`.
|
||||||
|
|
||||||
|
## Changing the icon set
|
||||||
|
|
||||||
|
Icons are generated, never hand-edited:
|
||||||
|
|
||||||
|
1. Add or replace the source SVG in `scripts/vendor/icons/<source>/` (`tabler/`, `simple/`, …). Keep license provenance in `THIRD_PARTY_LICENSES.md` accurate.
|
||||||
|
2. Regenerate and verify:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/build-icons.py
|
||||||
|
git diff --exit-code -- skills/diagram-design/assets/icons.html skills/diagram-design/references/primitive-icons.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Touching the import paths
|
||||||
|
|
||||||
|
- draw.io: `skills/diagram-design/scripts/drawio_extract.py` — must pass `scripts/verify-drawio-import.py`, which drives the extractor against `scripts/fixtures/sample-architecture.drawio` in all four container formats (raw XML, deflate+base64, PNG-embedded, SVG-embedded).
|
||||||
|
- Mermaid: `skills/diagram-design/scripts/mermaid_extract.py` — must pass `scripts/verify-mermaid-import.py`, which covers every supported grammar, multi-block Markdown, adversarial labels, trust-boundary behavior, resource caps, and named failures.
|
||||||
|
|
||||||
|
Both scripts treat their input as **untrusted data** — they never render, fetch, or execute source content. Keep it that way. If you add a grammar or a new security boundary, extend the corresponding verifier with a fixture before merging.
|
||||||
|
|
||||||
|
Documentation and wiring must stay in sync: the import references, `SKILL.md` §11, the slash commands in `commands/`, and the Pi prompt templates in `prompts/` each describe the same flows. The verifiers check this — keep both sides updated in one PR.
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Most of this repo *is* documentation. When behavior changes, update the affected reference files and the README in the same PR. Loose ends here are what the verifiers and reviewers will catch.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commit and PR conventions
|
||||||
|
|
||||||
|
Commit messages follow [Conventional Commits](https://www.conventionalcommits.org/): `type(scope): summary`, e.g. `fix(import): support current Mermaid syntax`, `ci: verify Mermaid imports`, `docs(onboarding): clarify the URL flow`. Keep summaries short and imperative.
|
||||||
|
|
||||||
|
A good PR:
|
||||||
|
|
||||||
|
- has a clear title and a description that says *what* changed and *why*;
|
||||||
|
- mentions how you tested it (which gates you ran);
|
||||||
|
- keeps generated and source files consistent (extractor + verifier + reference + command in one change);
|
||||||
|
- is rebased on `main` and green on CI.
|
||||||
|
|
||||||
|
## Questions?
|
||||||
|
|
||||||
|
Open a discussion or comment on the relevant issue. If it's about security, use the private reporting path in [SECURITY.md](SECURITY.md).
|
||||||
@@ -81,7 +81,7 @@ All 27 diagrams ship in three variants: minimal light, minimal dark, and full-ed
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
**Browse the live gallery:** open [`skills/diagram-design/assets/index.html`](skills/diagram-design/assets/index.html) in your browser to flip through all 27 diagrams with light / dark / full-editorial tabs.
|
**Browse the live gallery:** [cathrynlavery.github.io/diagram-design](https://cathrynlavery.github.io/diagram-design/) — or open [`skills/diagram-design/assets/index.html`](skills/diagram-design/assets/index.html) locally to flip through all 27 diagrams with light / dark / full-editorial tabs.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -408,6 +408,12 @@ Before drawing, ask: *would a reader learn more from this than from a well-writt
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Contributions are welcome — new diagram types, import grammar support, examples, docs, and tooling. See [CONTRIBUTING.md](CONTRIBUTING.md) for the validation gates and workflows, and [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for community standards.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
Made by **Cathryn Lavery** — founder of [BestSelf.co](https://bestself.co?utm_source=diagram-design&utm_medium=readme&utm_campaign=github&utm_content=bio). I write about AI, entrepreneurship, and designing nice-looking things at [littlemight.com](https://littlemight.com?utm_source=diagram-design&utm_medium=readme&utm_campaign=github&utm_content=bio) — blog + newsletter.
|
Made by **Cathryn Lavery** — founder of [BestSelf.co](https://bestself.co?utm_source=diagram-design&utm_medium=readme&utm_campaign=github&utm_content=bio). I write about AI, entrepreneurship, and designing nice-looking things at [littlemight.com](https://littlemight.com?utm_source=diagram-design&utm_medium=readme&utm_campaign=github&utm_content=bio) — blog + newsletter.
|
||||||
|
|||||||
@@ -314,6 +314,10 @@ def normalize_url(raw: str) -> str:
|
|||||||
def normalize_devicon(raw: str) -> str:
|
def normalize_devicon(raw: str) -> str:
|
||||||
"""Devicon SVGs have colored fills and a 128x128 or 32x32 viewBox.
|
"""Devicon SVGs have colored fills and a 128x128 or 32x32 viewBox.
|
||||||
Strip fills/classes and rewrite to 24x24 currentColor."""
|
Strip fills/classes and rewrite to 24x24 currentColor."""
|
||||||
|
viewbox_match = re.search(
|
||||||
|
r'\bviewBox\s*=\s*(["\'])(.*?)\1', raw, flags=re.IGNORECASE
|
||||||
|
)
|
||||||
|
viewbox = viewbox_match.group(2).strip() if viewbox_match else "0 0 128 128"
|
||||||
inner = re.search(r"<svg\b[^>]*>(.*?)</svg>", raw, flags=re.DOTALL | re.IGNORECASE)
|
inner = re.search(r"<svg\b[^>]*>(.*?)</svg>", raw, flags=re.DOTALL | re.IGNORECASE)
|
||||||
if not inner:
|
if not inner:
|
||||||
raise ValueError("no <svg> in Devicon payload")
|
raise ValueError("no <svg> in Devicon payload")
|
||||||
@@ -325,7 +329,7 @@ def normalize_devicon(raw: str) -> str:
|
|||||||
body = re.sub(r'\bclass="[^"]*"', 'fill="currentColor"', body)
|
body = re.sub(r'\bclass="[^"]*"', 'fill="currentColor"', body)
|
||||||
body = re.sub(r"\s+", " ", body).strip()
|
body = re.sub(r"\s+", " ", body).strip()
|
||||||
return (
|
return (
|
||||||
'<svg aria-hidden="true" width="24" height="24" viewBox="0 0 128 128" '
|
f'<svg aria-hidden="true" width="24" height="24" viewBox="{viewbox}" '
|
||||||
f'fill="currentColor">{body}</svg>'
|
f'fill="currentColor">{body}</svg>'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -17,8 +17,10 @@ PAIRS = [
|
|||||||
('…', '…'), # horizontal ellipsis
|
('…', '…'), # horizontal ellipsis
|
||||||
('•', '•'), # bullet
|
('•', '•'), # bullet
|
||||||
('→', '→'), # right arrow
|
('→', '→'), # right arrow
|
||||||
('↑', '←'), # left arrow
|
# CP1252 leaves byte 0x90 undefined, so spell the left-arrow corruption
|
||||||
('↑', '↑'), # up arrow (same prefix collision - rare)
|
# with a Unicode escape instead of embedding an invisible control character.
|
||||||
|
('\u00e2\u2020\u0090', '←'), # left arrow
|
||||||
|
('↑', '↑'), # up arrow
|
||||||
('↓', '↓'), # down arrow
|
('↓', '↓'), # down arrow
|
||||||
('≤', '≤'), # less-than-or-equal
|
('≤', '≤'), # less-than-or-equal
|
||||||
('≥', '≥'), # greater-than-or-equal
|
('≥', '≥'), # greater-than-or-equal
|
||||||
|
|||||||
Executable
+40
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Regression tests for Devicon viewBox preservation."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import importlib.util
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
BUILD = ROOT / "scripts" / "build-icons.py"
|
||||||
|
|
||||||
|
|
||||||
|
def load_build_module():
|
||||||
|
sys.dont_write_bytecode = True
|
||||||
|
spec = importlib.util.spec_from_file_location("diagram_design_build_icons", BUILD)
|
||||||
|
if spec is None or spec.loader is None:
|
||||||
|
raise AssertionError("could not load build-icons.py")
|
||||||
|
module = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(module)
|
||||||
|
return module
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
build_icons = load_build_module()
|
||||||
|
svg = '<svg viewBox="0 0 32 32"><path fill="#123456" d="M0 0h32v32H0z"/></svg>'
|
||||||
|
normalized = build_icons.normalize_devicon(svg)
|
||||||
|
if 'viewBox="0 0 32 32"' not in normalized:
|
||||||
|
raise AssertionError(f"source viewBox was not preserved: {normalized}")
|
||||||
|
if 'fill="#123456"' in normalized:
|
||||||
|
raise AssertionError("source fill escaped currentColor normalization")
|
||||||
|
if 'fill="currentColor"' not in normalized:
|
||||||
|
raise AssertionError("normalized icon does not inherit currentColor")
|
||||||
|
print("PASS: Devicon normalization preserves non-128 source viewBoxes")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Regression tests for the mojibake repair utility."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parent.parent
|
||||||
|
SCRIPT = ROOT / "scripts" / "fix-mojibake.py"
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
# These are the strings produced when UTF-8 arrow bytes are decoded as
|
||||||
|
# Windows-1252. Byte 0x90 is undefined in CP1252, hence the explicit escape.
|
||||||
|
source = "right →, left \u00e2\u2020\u0090, up ↑, down ↓\n"
|
||||||
|
expected = "right →, left ←, up ↑, down ↓\n"
|
||||||
|
with tempfile.TemporaryDirectory(prefix="diagram-design-mojibake-") as directory:
|
||||||
|
path = Path(directory) / "fixture.md"
|
||||||
|
path.write_text(source, encoding="utf-8")
|
||||||
|
result = subprocess.run(
|
||||||
|
[sys.executable, str(SCRIPT), str(path)],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
check=False,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
raise AssertionError(f"repair failed: {result.stdout}{result.stderr}")
|
||||||
|
actual = path.read_text(encoding="utf-8")
|
||||||
|
if actual != expected:
|
||||||
|
raise AssertionError(f"expected {expected!r}, got {actual!r}")
|
||||||
|
print("PASS: mojibake arrow sequences repair to the intended directions")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in New Issue
Block a user