From 02f52986c01848951f1642aa80211ca89d32f5ff Mon Sep 17 00:00:00 2001 From: Vitaliy Filyuchkov Date: Wed, 12 Aug 2026 11:18:34 +0300 Subject: [PATCH 1/4] docs(contributing): add CONTRIBUTING, code of conduct and issue/PR templates Add CONTRIBUTING.md covering all validation gates and contribution workflows, CODE_OF_CONDUCT.md (Contributor Covenant 2.1), GitHub issue templates (bug/feature), and a pull request template; add a Contributing section to the README. --- .github/ISSUE_TEMPLATE/bug_report.yml | 66 ++++++++++ .github/ISSUE_TEMPLATE/config.yml | 5 + .github/ISSUE_TEMPLATE/feature_request.yml | 45 +++++++ .github/PULL_REQUEST_TEMPLATE.md | 26 ++++ CODE_OF_CONDUCT.md | 134 ++++++++++++++++++++ CONTRIBUTING.md | 136 +++++++++++++++++++++ README.md | 6 + 7 files changed, 418 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..66a369a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -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 ` + 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 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..85c4c6c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -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. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..926bd55 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -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" \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..58e3005 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,26 @@ +## What does this PR do? + + + +## Validation gates + + + +- [ ] `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 + + \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..f7f99e2 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -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 \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..e6e6e2d --- /dev/null +++ b/CONTRIBUTING.md @@ -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 `` must satisfy the contract enforced by the linter: + +1. `role="img"` and `aria-labelledby` naming the `` **and** ``. +2. `` 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). \ No newline at end of file diff --git a/README.md b/README.md index f9b938e..2912033 100644 --- a/README.md +++ b/README.md @@ -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 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. From af6bc627997fd1b58a201e0bcaddf204f0dfbb68 Mon Sep 17 00:00:00 2001 From: Mr-Neutr0n <harikp2002@gmail.com> Date: Wed, 12 Aug 2026 15:52:47 +0530 Subject: [PATCH 2/4] fix: repair mojibake arrow directions Signed-off-by: Mr-Neutr0n <harikp2002@gmail.com> --- .github/workflows/ci.yml | 3 +++ scripts/fix-mojibake.py | 6 ++++-- scripts/test-fix-mojibake.py | 40 ++++++++++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 scripts/test-fix-mojibake.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65f74cc..084bf01 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,9 @@ jobs: - name: Verify accessible SVG contract run: python3 scripts/test-lint-a11y.py + - name: Verify mojibake repair utility + run: python3 scripts/test-fix-mojibake.py + - name: Run skin linter run: python3 scripts/lint-skin.py --all --baseline diff --git a/scripts/fix-mojibake.py b/scripts/fix-mojibake.py index 856cf17..4334fa7 100644 --- a/scripts/fix-mojibake.py +++ b/scripts/fix-mojibake.py @@ -17,8 +17,10 @@ PAIRS = [ ('…', '…'), # horizontal ellipsis ('•', '•'), # bullet ('→', '→'), # right arrow - ('↑', '←'), # left arrow - ('↑', '↑'), # up arrow (same prefix collision - rare) + # CP1252 leaves byte 0x90 undefined, so spell the left-arrow corruption + # with a Unicode escape instead of embedding an invisible control character. + ('\u00e2\u2020\u0090', '←'), # left arrow + ('↑', '↑'), # up arrow ('↓', '↓'), # down arrow ('≤', '≤'), # less-than-or-equal ('≥', '≥'), # greater-than-or-equal diff --git a/scripts/test-fix-mojibake.py b/scripts/test-fix-mojibake.py new file mode 100644 index 0000000..da4940e --- /dev/null +++ b/scripts/test-fix-mojibake.py @@ -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()) From 99bc4cb0bed37276fdffa376929fcdd6090e1244 Mon Sep 17 00:00:00 2001 From: Mr-Neutr0n <harikp2002@gmail.com> Date: Wed, 12 Aug 2026 17:12:33 +0530 Subject: [PATCH 3/4] fix(icons): preserve Devicon viewBox dimensions Signed-off-by: Mr-Neutr0n <harikp2002@gmail.com> --- .github/workflows/ci.yml | 3 +++ scripts/build-icons.py | 6 ++++- scripts/test-build-icons-devicon.py | 40 +++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) create mode 100755 scripts/test-build-icons-devicon.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65f74cc..5cbecae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,6 +29,9 @@ jobs: - name: Verify accessible SVG contract run: python3 scripts/test-lint-a11y.py + - name: Verify Devicon normalization + run: python3 scripts/test-build-icons-devicon.py + - name: Run skin linter run: python3 scripts/lint-skin.py --all --baseline diff --git a/scripts/build-icons.py b/scripts/build-icons.py index b191b2a..e78045b 100644 --- a/scripts/build-icons.py +++ b/scripts/build-icons.py @@ -292,6 +292,10 @@ def normalize_url(raw: str) -> str: def normalize_devicon(raw: str) -> str: """Devicon SVGs have colored fills and a 128x128 or 32x32 viewBox. 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) if not inner: raise ValueError("no <svg> in Devicon payload") @@ -303,7 +307,7 @@ def normalize_devicon(raw: str) -> str: body = re.sub(r'\bclass="[^"]*"', 'fill="currentColor"', body) body = re.sub(r"\s+", " ", body).strip() 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>' ) diff --git a/scripts/test-build-icons-devicon.py b/scripts/test-build-icons-devicon.py new file mode 100755 index 0000000..ecb57e5 --- /dev/null +++ b/scripts/test-build-icons-devicon.py @@ -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()) From 3ef39baba87cc475d1f3fb78866abbd8bc4a40f8 Mon Sep 17 00:00:00 2001 From: Saveena Solanki <saveenas@iiitd.ac.in> Date: Wed, 12 Aug 2026 21:19:16 +0530 Subject: [PATCH 4/4] ci: deploy live gallery to GitHub Pages and fix README link The README's "Browse the live gallery" link pointed at the raw HTML blob, which GitHub renders as source code rather than the gallery. Add a Pages deployment workflow that publishes skills/diagram-design/assets as a static site, and point the README at the deployed gallery (closes #33). --- .github/workflows/pages.yml | 41 +++++++++++++++++++++++++++++++++++++ README.md | 2 +- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/pages.yml diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..29cc633 --- /dev/null +++ b/.github/workflows/pages.yml @@ -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 diff --git a/README.md b/README.md index f9b938e..ea68ff1 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ All 27 diagrams ship in three variants: minimal light, minimal dark, and full-ed </tr> </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. ---